diff --git a/docs/proposals/bch-functions-team-review.md b/docs/proposals/bch-functions-team-review.md new file mode 100644 index 00000000..40cc2427 --- /dev/null +++ b/docs/proposals/bch-functions-team-review.md @@ -0,0 +1,79 @@ +# BCH Functions Team Review + +This directory now contains the maintainer review packet for the BCH functions work. + +Recommended reading order: + +1. [functions-vs-next-review.md](./functions-vs-next-review.md) +2. [functions-implementation-spec.md](./functions-implementation-spec.md) +3. [functions-security-and-release-review.md](./functions-security-and-release-review.md) + +Historical/internal notes remain available if needed: + +- [library-integration-plan.md](./library-integration-plan.md) +- [internal-functions-review.md](./internal-functions-review.md) + +## What This Packet Covers + +The current branch implements: + +- `public` and `internal` functions inside `contract` +- `library` as a non-spendable helper container +- BCH `OP_DEFINE` / `OP_INVOKE` lowering for local and imported helpers +- transitive `contract -> library -> library` imports +- helper-aware debug metadata and nested-frame debugging behavior + +## Review Focus + +The highest-value review questions for the CashScript team are: + +1. Is the `contract` / `library` split the right long-term source model? +2. Are the helper restrictions conservative enough for a release branch? +3. Is canonicalization by resolved library identity the right import behavior? +4. Is the helper-frame debug model sufficiently explicit and trustworthy? +5. Is the branch scoped and shaped correctly relative to the current `next` baseline? + +## Code Areas To Review + +The most important implementation files are: + +- `packages/cashc/src/compiler.ts` +- `packages/cashc/src/imports.ts` +- `packages/cashc/src/generation/GenerateTargetTraversal.ts` +- `packages/cashscript/src/debugging.ts` +- `packages/cashscript/src/Errors.ts` +- `packages/utils/src/artifact.ts` + +The most important tests are: + +- `packages/cashc/test/compiler/compiler.test.ts` +- `packages/cashc/test/generation/generation.test.ts` +- `packages/cashscript/test/debugging.test.ts` + +## Validation Summary + +This branch was brought to a clean state with: + +- root `yarn test` +- root `yarn build` +- root `yarn lint` + +It also includes adversarial regressions around: + +- nested helper failure attribution +- ambiguous helper-frame identity +- shared transitive import graphs +- duplicate helper names across imported libraries + +## Showcase Examples + +For maintainers reviewing the feature with concrete source examples: + +- local internal helpers: + - `examples/helper-functions.cash` + - `examples/helper-functions.ts` +- imported helper libraries: + - `examples/imported-helper-functions.cash` + - `examples/math-helpers.cash` + - `examples/parity-helpers.cash` + - `examples/imported-helper-functions.ts` diff --git a/docs/proposals/functions-implementation-spec.md b/docs/proposals/functions-implementation-spec.md new file mode 100644 index 00000000..cb73af9f --- /dev/null +++ b/docs/proposals/functions-implementation-spec.md @@ -0,0 +1,295 @@ +# BCH Functions Implementation Spec + +## Purpose + +This document describes the current implementation of BCH function support on top of the current `next` branch. + +It is written as an implementation/spec handoff for maintainers who want to review: + +- source-level language semantics +- compiler lowering strategy +- helper import behavior +- debug/runtime metadata behavior +- safety boundaries and explicit non-goals + +This document describes the implementation as it exists in the branch, not just an aspirational plan. + +## Scope + +Relative to current `next`, the implementation adds: + +- explicit function visibility inside `contract`: `public` and `internal` +- first-class lowering of helper functions to BCH `OP_DEFINE` / `OP_INVOKE` +- `library` as a first-class non-spendable top-level container +- support for local helper functions and imported helper functions +- transitive `contract -> library -> library` imports +- helper-aware debug metadata and nested-frame attribution in the SDK + +The implementation intentionally does not add: + +- standalone spendable imported modules +- imports of `contract` files +- recursive helper call graphs +- signature-check builtins inside internally invoked helpers +- contract-constructor capture inside helper functions + +## Source-Level Model + +### `contract` + +A `contract` is the spendable top-level unit. + +A contract may contain: + +- `public` functions +- `internal` functions + +`public` functions: + +- appear in the ABI +- are exposed through the SDK as unlock methods +- act as external spend entrypoints + +`internal` functions: + +- do not appear in the ABI +- are not directly exposed as unlock methods +- are compiled as helper frames and may be invoked from other functions + +Inside a `contract`, omitted visibility is still accepted and treated as `public`. + +### `library` + +A `library` is a non-spendable top-level unit. + +A library: + +- cannot declare constructor parameters +- cannot compile to a spendable artifact +- cannot define public entrypoints +- may only define `internal` helper functions + +This is the main container-level guardrail that prevents imported helper files from becoming independent spend surfaces. + +## Import Model + +### Allowed forms + +Supported forms are: + +- `contract -> library` +- `library -> library` + +Imports are: + +- compile-time only +- relative-path only +- namespace-qualified at source level + +Example: + +```cash +import "./math.cash" as Math; + +contract Vault() { + function spend(int value) public { + require(Math.isEven(value)); + } +} +``` + +Imported library: + +```cash +library MathHelpers { + function isEven(int value) internal { + require(value % 2 == 0); + } +} +``` + +### Forbidden forms + +The compiler rejects: + +- importing a `contract` +- importing a library with `public` functions +- duplicate aliases at the same scope +- circular transitive library imports +- namespaced external helper references from inside a library +- library references to non-library local functions + +### Canonicalization strategy + +Transitive libraries are canonicalized by resolved file identity rather than duplicated per alias path. + +This matters for diamond graphs and shared leaves: + +- the same resolved library file is compiled once +- helper names are mangled from canonical library identity, not alias-path repetition +- shared leaves are not duplicated just because they are reachable from multiple branches + +That decision was made to avoid hidden bytecode/opcount inflation in reusable helper graphs. + +## Compiler Model + +### Entrypoints vs helper frames + +The compiler distinguishes between: + +- public dispatcher entrypoints +- helper frames + +Helper frames may come from: + +- local `internal` functions +- local `public` functions that are also invoked internally +- imported library `internal` functions + +### Reachability + +Reachability is computed from public entrypoints. + +This means: + +- unreachable internal-only call chains are not emitted +- unreachable imported helper chains are not emitted +- the BCH function table is driven by actual entrypoint reachability rather than by all syntactically declared helpers + +### Lowering + +Reachable helper functions are compiled to separate bytecode fragments and emitted with: + +- `OP_DEFINE` +- `OP_INVOKE` + +Public dispatcher flow remains on the normal ABI dispatch path. + +Invoked public functions reuse the same helper-frame body rather than duplicating a second compiled body just for internal invocation. + +### VM target enforcement + +If helper opcodes are required, the compiler: + +- upgrades inferred target requirements to at least `BCH_2026_05` +- rejects explicitly lower targets +- still permits `BCH_SPEC` + +This prevents artifacts from claiming an older VM target while containing BCH function opcodes. That enforcement is part of the functions delta, not baseline `next` behavior. + +## Safety Restrictions + +### Helper-function restrictions + +The implementation intentionally rejects: + +- direct recursion +- mutual recursion +- signature-check builtins inside internally invoked functions +- direct constructor-parameter capture inside helper functions + +These are conservative restrictions introduced by the functions implementation because the current compiler/runtime/debugging model can defend these boundaries cleanly. + +### Why these restrictions exist + +The guiding rule is: + +- if the compiler/runtime cannot model a helper pattern safely and inspectably, it should reject it rather than guess + +Specific reasons: + +- recursion complicates bounded codegen and debug semantics +- signature-check builtins in nested helper frames complicate signing and runtime attribution +- constructor capture makes helper behavior less explicit and less auditable + +## Debugging And Artifact Model + +### Frame-aware metadata + +Artifacts now carry richer debug metadata for helper-aware execution. + +The implementation includes: + +- root-frame debug metadata +- helper frame metadata in `debug.frames` +- per-log frame metadata +- per-stack-item frame metadata +- per-require frame metadata +- source provenance for imported helper frames + +Developer-facing debug output prefers readable source basenames, while internal metadata keeps full path provenance where available. + +### Frame identity + +Helper frames are identified by explicit frame ids plus frame bytecode. + +The implementation also includes a compiler safeguard: + +- if two distinct helper functions would compile to identical helper bytecode, compilation fails + +That guard exists because runtime frame attribution is unsafe if two helper frames are bytecode-identical. + +### Nested failure attribution + +The SDK now resolves helper logs and helper failures against the active frame. + +Implemented behavior: + +- internal helper logs resolve to the helper source line +- helper `require(...)` failures resolve to the helper source line and statement +- nested non-`require` helper failures resolve as evaluation failures, not as guessed require failures +- final verify failures caused by a nested helper final `require(...)` are attributed back to that helper require when this can be proven safely + +The important audit constraint is: + +- the SDK should not speculate broadly about require attribution +- it should only attribute a nested require when there is a safe frame-local match + +## Important Edge Cases Covered + +The branch includes coverage for: + +- local internal helpers reachable from public functions +- unreachable helper chains not being emitted +- public-to-public invocation while preserving ABI exposure +- shared helper-frame reuse for invoked public functions +- imported library helpers lowering to BCH function opcodes +- transitive imports +- shared-leaf canonicalization in diamond-style graphs +- overlapping helper names across imported libraries +- internal helper logs +- internal helper require failures +- nested non-require helper failures not being misattributed to earlier require statements +- ambiguous identical helper bytecode rejection + +## Behavior That Is Intentionally Transitional + +For `library`, the implementation is strict: + +- explicit `internal` is required +- `public` is rejected + +## Reviewer Checklist + +The highest-value review questions for the main CashScript team are: + +1. Is `contract` + `library` the right long-term source model? +2. Are the current helper restrictions conservative enough for merge? +3. Is explicit `internal` in `library` the right strictness level? +4. Is canonicalization by resolved library identity the right import strategy? +5. Is the helper debug model sufficiently explicit and stable for external users? +6. Is the current `contract` visibility behavior acceptable for the first functions release line? + +## Bottom Line + +The current implementation treats BCH function opcodes as first-class compiler/runtime behavior rather than an afterthought. + +The central semantics are: + +- spend surface belongs to `contract` +- reusable helper surface belongs to `internal` functions +- imported helper code belongs to `library` +- helper execution is unified whether the helper is local or imported +- imported libraries cannot become independent spend paths + +That is the design this branch implements and the basis on which it should be reviewed. diff --git a/docs/proposals/functions-security-and-release-review.md b/docs/proposals/functions-security-and-release-review.md new file mode 100644 index 00000000..d6b2630d --- /dev/null +++ b/docs/proposals/functions-security-and-release-review.md @@ -0,0 +1,206 @@ +# BCH Functions Security And Release Review + +## Purpose + +This document records the security posture, review findings addressed in the branch, and the production-readiness checks that were used to harden the BCH functions implementation. + +It is intended for maintainers doing scrutiny before merge or release. + +## Threat Model + +The implementation was reviewed against these failure classes: + +- accidental creation of new spend paths +- imported helpers behaving like independent contracts +- helper-frame debugging ambiguity +- wrong source attribution for nested helper failures +- hidden bytecode growth from transitive imports +- helper-name collisions across import graphs +- dead helper code being emitted unnecessarily + +## Main Guardrails + +### Spend-surface guardrails + +Implemented constraints: + +- only `contract` is spendable +- `library` cannot compile as an artifact +- `library` cannot declare constructor parameters +- `library` cannot expose `public` functions +- imported `contract` files are rejected + +These are the primary protections against imported helpers becoming independent authorization surfaces. + +### Helper-safety guardrails + +Implemented constraints: + +- helper functions cannot recurse +- helper functions cannot mutually recurse +- helper functions cannot use signature-check builtins +- helper functions cannot capture contract constructor parameters + +These are conservative restrictions intended to keep helper execution explicit and auditable. + +### Debugging-integrity guardrails + +Implemented protections: + +- helper frames carry explicit frame ids and frame bytecode +- compiler rejects contracts whose helper frames would be bytecode-identical +- nested helper logs and helper requires resolve against helper-frame metadata +- nested non-require failures are not broadly guessed as require failures + +This matters because ambiguous debugging in covenant tooling is not just a DX issue; it can hide real behavioral faults during review. + +## Audit Findings Addressed + +### 1. Ambiguous helper-frame identity + +Original concern: + +- helper-frame identity was too dependent on frame bytecode +- two different helpers with identical bytecode could cause incorrect log/error attribution + +Resolution: + +- helper debug metadata now includes explicit frame ids +- runtime matching uses helper-frame metadata +- compiler rejects helper-frame bytecode collisions across distinct helpers + +Result: + +- helper attribution is no longer allowed to proceed in an ambiguous bytecode-collision state + +### 2. Shared transitive-library duplication + +Original concern: + +- the same imported shared leaf could be duplicated per import path +- that would inflate bytecode and opcount unexpectedly in diamond graphs + +Resolution: + +- transitive libraries are canonicalized by resolved file identity +- shared leaves are compiled once and reused + +Result: + +- reusable helper graphs do not silently grow script size just because they are reached through more than one alias path + +### 3. Misattribution of nested non-require failures + +Original concern: + +- nested helper failures could be misreported as if an earlier helper `require(...)` failed + +Resolution: + +- speculative broad fallback was removed +- nested helper final-verify attribution now only maps to a helper require when that mapping can be made safely +- explicit regression coverage now checks that a later non-require helper failure is not blamed on an earlier helper require + +Result: + +- nested failure reporting is more honest and safer for auditing + +## Adversarial Cases Covered + +The branch now includes targeted coverage for failure-oriented cases, not only happy paths. + +### Import graph adversarial cases + +- duplicate top-level aliases rejected +- duplicate helper names across imported libraries supported safely +- circular transitive imports rejected +- shared-leaf transitive libraries canonicalized +- imported namespaced external helper references rejected +- imported references to non-library local functions rejected + +### Helper execution adversarial cases + +- unreachable helper chains not emitted +- ambiguous identical helper bytecode rejected at compile time +- internal helper logs attributed to helper source line +- internal helper require failures attributed to helper source line and statement +- nested non-require helper failures stay evaluation failures + +### Container and visibility adversarial cases + +- omitted library visibility is rejected +- public functions inside a library are rejected +- imported contracts are rejected + +## Residual Tradeoffs + +These are not open bugs in the branch, but they are still design choices worth explicit maintainer review. + +### Explicit `internal` in `library` + +Current behavior: + +- `library` functions must explicitly declare `internal` + +Tradeoff: + +- stricter and clearer for review +- slightly more verbose than treating `library` as implicitly helper-only + +### Helper restrictions + +Current behavior: + +- helper restrictions are intentionally conservative + +Tradeoff: + +- safer for release +- narrower than everything BCH opcodes could theoretically support + +## Production-Readiness Verification + +The branch was validated with: + +- full root `yarn test` +- full root `yarn build` +- full root `yarn lint` + +It was also hardened with focused suites around: + +- compiler semantics +- generation +- AST/source round-tripping +- helper-aware debugging + +## Recommended Maintainer Review Focus + +If the main CashScript team wants to review this efficiently, the highest-value areas are: + +1. `packages/cashc/src/compiler.ts` +2. `packages/cashc/src/imports.ts` +3. `packages/cashc/src/generation/GenerateTargetTraversal.ts` +4. `packages/cashscript/src/debugging.ts` +5. `packages/cashscript/src/Errors.ts` +6. `packages/utils/src/artifact.ts` +7. `packages/cashscript/test/fixture/debugging/debugging_contracts.ts` + +And the highest-value tests to inspect are: + +1. `packages/cashc/test/compiler/compiler.test.ts` +2. `packages/cashc/test/generation/generation.test.ts` +3. `packages/cashscript/test/debugging.test.ts` + +## Bottom Line + +This branch is not just a feature spike anymore. + +It has: + +- a defined spend-surface model +- explicit imported-helper constraints +- canonical transitive imports +- nested helper-aware debugging +- adversarial tests aimed at ways to break helper attribution and helper import safety + +That is the implementation state maintainers should review. diff --git a/docs/proposals/functions-vs-next-review.md b/docs/proposals/functions-vs-next-review.md new file mode 100644 index 00000000..bc8d6538 --- /dev/null +++ b/docs/proposals/functions-vs-next-review.md @@ -0,0 +1,136 @@ +# BCH Functions Vs Current `next` + +## Purpose + +This document summarizes what this branch adds relative to the current team-maintained `next` branch, and how those additions relate to the team’s current direction. + +It is intentionally scoped to the delta that matters for review. + +## Current Base + +Current reviewed base: + +- `next` at `84d858f` + +This branch is built on top of that base through the existing merge into `feature/functions-only`, then the later `library` and hardening work. + +## What `next` Already Has + +Current `next` already includes: + +- the newer loop implementation work +- the current SDK/network changes from the team +- the baseline parser/compiler/runtime behavior without BCH helper functions + +That means this branch should be reviewed as: + +- functions work layered on top of current `next` +- not as an alternative to the team’s loop work + +## What This Branch Adds Beyond `next` + +### 1. Contract-local helper functions + +This branch adds explicit function classification inside contracts: + +- `public` +- `internal` + +And lowers reachable helper calls through: + +- `OP_DEFINE` +- `OP_INVOKE` + +This is the core language/compiler change. + +### 2. Non-spendable `library` containers + +This branch adds a second top-level container: + +- `library` + +`library` exists to make imported helper code non-spendable by construction. + +That is a deliberate design choice from our local work: + +- imported helper files should complement a contract +- imported helper files should not define their own spend surface + +### 3. Transitive helper imports + +This branch adds: + +- `contract -> library` +- `library -> library` + +with: + +- cycle rejection +- alias checks +- canonicalization by resolved file identity + +### 4. Helper-aware debugging and artifact metadata + +This branch adds helper-frame metadata and SDK-side helper-aware attribution for: + +- logs +- require failures +- nested helper evaluation failures + +This matters because helper functions without usable debugging are not reviewable or production-ready enough for other teams. + +### 5. Conservative helper restrictions + +This branch explicitly rejects helper patterns that are currently too risky or too underspecified: + +- recursion +- mutual recursion +- signature-check builtins in helpers +- contract-constructor capture in helpers + +## How This Fits The Team’s Current Direction + +### Loops + +The main team is already carrying the loop work on `next`. + +Our branch does not try to compete with that work. It assumes those loop changes are the baseline and focuses on the other major BCH 2026 opcode area: + +- user-defined function frames + +### Compiler direction + +The branch follows the same broader philosophy we discussed locally: + +- BCH opcodes should be first-class compiler targets +- source constructs should map cleanly onto those opcodes +- the compiler should make spend surfaces and helper surfaces explicit + +In that sense, this work is aligned with the team’s broader compiler direction rather than being an unrelated experiment. + +### Safety posture + +The strongest local design decision was: + +- helper reuse should be enabled +- hidden new spend surfaces should not be enabled + +That is why `library` is non-spendable and why helper restrictions are deliberately conservative. + +## Main Things The Team Should Scrutinize + +1. Whether `library` is the right source-level container for imported helper code. +2. Whether explicit `public` / `internal` is the right contract-level function model. +3. Whether canonicalizing shared transitive libraries by resolved file identity is the right import strategy. +4. Whether the current helper restrictions are conservative in the right places. +5. Whether the helper debug metadata is stable enough for downstream tooling and users. + +## Bottom Line + +Relative to current `next`, this branch is primarily: + +- BCH helper-function support +- non-spendable imported helper libraries +- helper-aware debugging and hardening + +It is not a competing loops branch. It is the local functions track, updated to fit the team’s current `next` baseline. diff --git a/docs/proposals/internal-functions-review.md b/docs/proposals/internal-functions-review.md new file mode 100644 index 00000000..14832245 --- /dev/null +++ b/docs/proposals/internal-functions-review.md @@ -0,0 +1,175 @@ +# Internal Functions Review + +## Scope + +This note records review findings, design constraints, and follow-up ideas for the local `feature/functions-only` strategy. + +It is intended as a future reference for: + +- continuing the internal-function work +- evaluating import-based helper files on top of internal functions +- reviewing attack surfaces and spend-path risks introduced by BCH function support + +The current strategy is: + +- internal functions are compiled to BCH 2026 function frames using `OP_DEFINE` and `OP_INVOKE` +- public functions remain the contract ABI surface +- internal functions are meant to behave like reusable helper logic, not user-facing entrypoints + +## Current Design Direction + +### Internal functions + +The current branch treats internal functions as helper bodies that can be invoked from public entrypoints or from other invoked functions. + +Current safety checks already block several dangerous cases: + +- recursive call graphs +- signature-check built-ins inside invoked functions +- direct contract-parameter capture inside invoked functions + +This is a sound base. + +### Future imported helper files + +The intended direction for imported function files should stay narrow: + +- imported files should act like utility/helper files +- imported files should not define spendable/user-facing contract entrypoints +- imported code should complement a main contract rather than behave like a second contract + +This is conceptually similar to OpenZeppelin utility modules: + +- shared helper logic +- no independent public spend surface +- explicit reuse from a main contract + +The practical implication is: + +- imported files should contribute only helper/internal functions +- imported files should not expose ABI/public functions +- imported files should not define independent contract spend paths +- imported files should eventually be modeled as `library`, not as ordinary spendable contracts + +## Findings + +### 1. Nested-frame debugging is still incomplete for non-`require` failures + +Severity: Medium + +Relevant code: + +- `packages/cashscript/src/debugging.ts` +- `packages/cashscript/src/Errors.ts` + +The branch adds frame bytecode metadata and improves nested `require(...)` attribution, but unmatched evaluation failures still fall back to root-frame sourcemap lookup. + +This means failures inside invoked frames that are not cleanly matched to a `require(...)` can still be reported against the wrong source location. + +Examples to care about: + +- introspection/indexing errors inside an invoked helper +- malformed split/slice behavior inside an invoked helper +- any VM failure path not tied back to a recorded `RequireStatement` + +Recommendation: + +- treat nested-frame evaluation errors as first-class debug cases +- add frame-aware source resolution rather than only frame-aware `require`/log matching +- add tests for internal helper failures that are not ordinary `require(...)` failures + +### 2. Locking-script start detection in debugging is brittle + +Severity: Medium + +Relevant code: + +- `packages/cashscript/src/debugging.ts` + +The comments describe bytecode-based root-frame detection as the safe approach once nested function frames exist, but the current implementation still prefers the last `ip === 0 && controlStack.length === 0` match. + +That is a brittle heuristic if nested frames can also satisfy that shape. + +Why this matters: + +- log slicing can start from the wrong frame +- error attribution can be anchored incorrectly +- correctness depends on current VM debug behavior rather than a stable discriminator + +Recommendation: + +- prefer bytecode/frame identity first +- keep the heuristic fallback only as a last resort +- add tests where nested invoked frames also begin at `ip === 0` + +### 3. Public-to-public invocation duplicates function bodies + +Severity: Medium + +Relevant code: + +- `packages/cashc/src/generation/GenerateTargetTraversal.ts` +- `packages/cashc/test/generation/generation.test.ts` + +The current implementation emits invoked functions as `OP_DEFINE` frames while still inlining public ABI functions in the top-level dispatcher. + +If a public function is called by another public function, its logic is effectively represented twice: + +- once as a dispatch branch +- once as a function frame + +Why this matters: + +- larger bytecode +- higher risk of hitting policy/size/resource limits +- more surface for future debugging drift + +Current design decision: + +- public-to-public behavior is acceptable semantically +- the concern is implementation quality and efficiency, not the feature itself + +Recommendation: + +- preserve public-to-public semantics +- later compile public ABI wrappers that invoke a shared body if we want to remove duplication cleanly + +## General Security Posture + +### What already looks good + +- `EnsureInvokedFunctionsSafeTraversal` blocks recursive helper graphs +- it blocks signature-check built-ins inside invoked functions +- it blocks contract-parameter capture inside invoked functions +- unreachable internal-only helpers are not emitted +- compiler target inference correctly detects `OP_DEFINE` / `OP_INVOKE` and requires BCH 2026 semantics + +### What still needs hardening + +- visibility mistakes should be made harder to miss in normal contracts +- nested-frame debugging should become frame-correct for all failure classes +- codegen should avoid duplicating public bodies where possible +- tests should cover more pathological/internal-frame error cases + +## Suggested Rules For Imported Helper Files + +If import-based helper files are built on top of this strategy, they should follow these restrictions: + +- imported files may only contribute helper/internal functions +- imported files may not define public ABI entrypoints +- imported files may not define independent spend paths +- imported helper functions must satisfy the same safety checks as local invoked functions +- imported helper functions should be parameter-driven and context-closed +- imported helper functions should not capture contract constructor parameters +- imported helper functions should not contain signature-check built-ins +- imported helper files should eventually be represented by a non-spendable `library` container + +This keeps imported files in the role of utility modules, not secondary contracts. + +## Recommended Next Steps + +1. Introduce first-class `library` semantics for imported helper code. +2. Add nested-frame failure tests for non-`require` VM errors. +3. Rework root-frame detection in debugging to prefer bytecode/frame identity. +4. Preserve public-to-public behavior, but reduce duplicated codegen later. +5. Keep imported files utility-only and non-spendable. diff --git a/docs/proposals/library-integration-plan.md b/docs/proposals/library-integration-plan.md new file mode 100644 index 00000000..f36e1054 --- /dev/null +++ b/docs/proposals/library-integration-plan.md @@ -0,0 +1,464 @@ +# Library Integration Plan + +## Purpose + +This document plans the next stage after `feature/functions-only`: + +- keep BCH function opcodes as first-class compilation targets +- preserve backward compatibility for existing contracts as much as possible +- introduce a non-spendable helper container for reusable imported logic + +The proposed helper container is `library`. + +This plan is intentionally architecture-focused. It records: + +- the design choices we are making +- what we are explicitly not doing +- where the compiler should enforce boundaries +- open questions and future gaps + +## Design Goals + +### Core goals + +- preserve current contract semantics for older contracts where practical +- treat `OP_DEFINE` and `OP_INVOKE` as first-class compiler outputs rather than a side feature +- separate spendable entrypoints from helper-only function bodies +- make imported helper code reusable without letting it define its own spend surface +- keep imported code deterministic, auditable, and easy to reason about + +### Non-goals for the first iteration + +- building a package ecosystem +- supporting arbitrary multi-contract import graphs +- allowing imported files to act as standalone spendable contracts +- reproducing Solidity inheritance/module behavior broadly + +## High-Level Model + +### `contract` + +A `contract` remains the normal spendable container. + +It may contain: + +- public entrypoint functions +- helper/internal functions + +It remains the source of: + +- constructor parameters +- ABI/public function surface +- top-level spend authorization paths + +### `library` + +A `library` is a non-spendable helper container. + +It may contain: + +- helper/internal functions only + +It must not contain: + +- public ABI entrypoints +- constructor parameters +- any independent spendable contract surface + +The compiler should treat library functions as helper frames only, never as dispatcher-selectable entry frames. + +## Why `library` Instead of Tightening Visibility Alone + +We do not want to break older contracts too aggressively by requiring all existing contracts to adopt locally introduced `public` / `internal` labels immediately. + +Using a first-class `library` container gives a stronger and cleaner boundary: + +- old `contract` behavior can stay largely compatible +- imported helper files can still be strongly constrained +- the spendable vs non-spendable distinction is modeled at the container level, not only by function annotations + +This fits the intended OpenZeppelin-like utility model better: + +- shared helper modules +- no independent user spend surface +- explicit reuse from a main contract + +## First-Class Opcode Strategy + +The important architectural decision is: + +- function opcodes are first-class execution primitives +- the compiler’s job is to map source-level function containers onto entry frames vs helper frames + +Under this model: + +- public contract functions become dispatcher-selectable entry frames +- helper/internal contract functions become helper frames +- library functions become helper frames only + +This avoids treating helper imports as string flattening first and semantics second. The compiler should explicitly understand: + +- which functions are entrypoints +- which functions are helper-only +- which containers are allowed to contribute entrypoints + +## Proposed Semantic Rules + +### Contract rules + +Regular contracts: + +- may define public entrypoints +- may define helper/internal functions +- may call their own helper functions +- may call imported library helper functions +- remain the only spendable top-level compilation units + +Compatibility policy: + +- existing omitted visibility behavior may remain for contracts if needed for backward compatibility +- helper/public distinction inside contracts can continue to evolve without forcing an immediate break on legacy code + +### Library rules + +Libraries: + +- may define helper/internal functions only +- may not define public ABI functions +- may not define constructor parameters +- may not compile as standalone spendable artifacts +- may only be imported into a contract or another library if we choose to support transitive library imports + +### Imported helper restrictions + +Imported helper functions should satisfy the same safety constraints as local invoked functions, plus the container-level rules above. + +That means imported helper functions must not: + +- expose dispatcher-selectable/public entrypoints +- reference contract constructor parameters +- use signature-check built-ins +- create their own independent spend authorization paths + +Important clarification: + +Imported helper functions may still contain checks such as `require(...)`. + +What is forbidden is not “having checks”. +What is forbidden is “being independently callable as a top-level spend surface”. + +## Recommended Compiler Shape + +### Phase 1: first-class `library` parsing + +Add grammar support for a second top-level container: + +- `contract` +- `library` + +The AST should represent container kind explicitly rather than hiding library semantics in preprocessing. + +Possible direction: + +- `SourceFileNode` contains a top-level container node +- introduce `LibraryNode` alongside `ContractNode` +- or generalize into a shared container abstraction if that reads cleanly + +### Phase 2: semantic container validation + +Add a container-aware semantic pass that enforces: + +- contracts may define entrypoints +- libraries may not define entrypoints +- libraries may not define constructor parameters +- libraries may only contain helper functions + +This is where “imported helpers cannot make their own transaction” should be enforced semantically. + +### Phase 3: entry-frame vs helper-frame lowering + +Refactor code generation around two explicit categories: + +- entry frames +- helper frames + +Desired compiler behavior: + +- contract public functions become entry frames +- contract helper functions become helper frames if reachable +- library functions become helper frames if reachable +- libraries never contribute dispatcher branches + +This should keep opcode lowering consistent with the source model. + +### Phase 4: import integration + +Imports should become a mechanism for bringing library helper frames into a contract’s reachable helper graph. + +For MVP: + +- import only `library` files +- import resolution should remain deterministic and narrow +- imported library functions should merge into the reachable helper graph +- imported library functions should never alter ABI/public dispatch directly + +## Import Strategy + +### MVP import policy + +Allowed: + +- relative imports only +- imported `library` files only +- namespace-qualified library use if we want source clarity +- deterministic compile-time integration + +Not allowed initially: + +- package-manager style resolution +- remote imports +- importing `contract` files +- imported public entrypoints +- imported independent spend surfaces + +### Syntax direction + +The likely source-level direction is something like: + +```cash +import "./math.cash" as Math; + +contract Vault(pubkey owner) { + function spend(sig s, int value) { + require(Math.isPositiveEven(value)); + } +} +``` + +Imported file: + +```cash +library Math { + function isPositiveEven(int value) { + require(isPositive(value)); + require(isEven(value)); + } + + function isPositive(int value) { + require(value > 0); + } + + function isEven(int value) { + require(value % 2 == 0); + } +} +``` + +The final choice on whether library functions need an explicit `internal` marker is still open. Since the container is already non-spendable, implicit helper-only semantics may be acceptable inside `library`. + +## Backward Compatibility Choices + +### What we want to preserve + +- existing `contract` files should continue to compile where possible +- existing omission of `public` should not automatically break older sources if we can avoid it +- helper functions inside normal contracts should remain supported + +### What we can tighten safely + +- imported helper files should be subject to stricter rules than ordinary contracts +- `library` can be strict from day one because it is new surface area +- “no spendable entrypoints in libraries” should be a hard rule, not a warning + +This gives us a compatibility-friendly path: + +- legacy `contract` behavior remains mostly stable +- new `library` behavior is deliberately constrained + +## Architectural Tradeoffs + +### Preprocessing vs first-class AST support + +The exploration branch used a preprocessing/flattening approach. + +That is attractive for speed of implementation, but weaker architecturally because: + +- spendable vs non-spendable semantics are implicit +- debugging and provenance become bolt-ons +- imports are resolved as source rewriting first rather than compiler semantics first + +For this strategy, first-class container semantics are preferred. + +### Visibility policy inside contracts + +We do not currently want to require all old contracts to add explicit `public` / `internal`. + +So the likely direction is: + +- keep contract visibility behavior reasonably compatible +- rely on `library` to enforce strong imported-helper boundaries +- revisit stricter explicit visibility later if the safety tradeoff justifies it + +### Public-to-public invocation + +Current understanding: + +- public-to-public behavior is not inherently wrong +- it matches the idea that one contract function may route into another function body + +So this should not be treated as a semantic bug. + +The remaining concern is implementation quality: + +- avoid needless duplication of public bodies if a cleaner shared-body architecture is possible + +## Security Boundaries To Enforce + +### Spend-surface boundary + +The most important rule: + +- only contract entry frames may define top-level spend authorization paths + +Libraries must not be able to create: + +- ABI-visible entrypoints +- dispatcher branches +- standalone transaction authorization surfaces + +### Context-capture boundary + +Helper/library functions should remain reusable and context-closed. + +They must not silently depend on: + +- constructor parameters +- hidden contract-local authorization assumptions +- signature validation side effects + +### Determinism boundary + +Imports should remain deterministic. + +We should avoid: + +- flexible package resolution +- source drift across environments +- import systems that obscure what helper code was actually compiled + +## Testing Plan + +### Required tests for `library` + +- library file with helper functions compiles only when imported into a contract +- library file cannot define a public spendable entrypoint +- library file cannot define constructor parameters +- imported library helper can be called from a contract +- imported library helper cannot capture contract constructor parameters +- imported library helper cannot use signature-check built-ins +- imported library helper graph rejects cycles + +### Required tests for compiler architecture + +- contract public functions become dispatcher entries +- contract helper functions do not become ABI entries +- library functions never become dispatcher entries +- imported library functions lower to helper frames only +- imported helper failures are reported with correct source attribution once debugging is extended + +## Open Questions + +### 1. Should library functions require explicit `internal`? + +Options: + +- yes, for symmetry with contracts +- no, because the container already guarantees helper-only semantics + +Current leaning: + +- do not require explicit `internal` inside `library` unless it materially simplifies the compiler + +### 2. Should transitive library imports be allowed in v1? + +Options: + +- no, keep one-level imports only for simplicity +- yes, but only for relative local libraries with strict cycle rejection + +Current leaning: + +- start with one-level imports if implementation simplicity matters +- add transitive imports later if needed + +### 3. How should source-level debugging work across libraries? + +Current internal-function debugging already needs more frame-aware handling. +Library support increases that need further. + +Open question: + +- do we postpone full multi-file source fidelity until after semantic integration +- or do we require it before considering library support complete + +Current leaning: + +- semantic/container correctness first +- better multi-file/frame-aware debugging as a required follow-up before calling the feature mature + +## Future Gaps + +### 1. Nested-frame debugging + +The current internal-function strategy still has known gaps around nested-frame failure attribution. + +Library support should not ship as “complete” without improving: + +- frame-aware root detection +- frame-aware evaluation error attribution +- file-aware source mapping if imports are supported + +### 2. Shared-body codegen + +If public-to-public calls remain supported, codegen should eventually avoid compiling the same public logic twice. + +This is an optimization and architectural cleanliness task, not a semantic blocker. + +### 3. Import provenance + +If imports are introduced, artifact/debug metadata should eventually make it easy to answer: + +- which library files were used +- which helper body a failure came from +- what exact source set produced the artifact + +### 4. Formal container semantics + +Right now the AST/grammar are contract-centric. + +Library support likely deserves: + +- explicit container kinds in the grammar +- explicit container nodes in the AST +- explicit semantic passes for entrypoint eligibility + +## Recommended Implementation Order + +1. Introduce first-class `library` grammar and AST support. +2. Add semantic validation that libraries are non-spendable helper containers. +3. Refactor codegen around entry frames vs helper frames. +4. Keep regular contract compatibility as intact as possible. +5. Add minimal deterministic library import support. +6. Improve debugging/provenance for nested frames and imported helpers. + +## Summary + +The recommended direction is: + +- keep old `contract` behavior mostly compatible +- introduce `library` as a strict non-spendable helper container +- keep BCH function opcodes as first-class compiler targets +- model entry frames vs helper frames explicitly in the compiler +- ensure imported helper code can never define its own spend surface + +This gives us a clean path from `functions-only` toward reusable helper modules without blurring the boundary between utility logic and spend authorization. diff --git a/examples/README.md b/examples/README.md index 803a8015..61493359 100644 --- a/examples/README.md +++ b/examples/README.md @@ -3,6 +3,12 @@ This folder contains a number of example CashScript contracts to show off its fu The "Hello World" of cash contracts is defining the P2PKH pattern inside a cash contract, which can be found under [`p2pkh.cash`](/examples/p2pkh.cash). Its usage can be found under [`p2pkh.ts`](/examples/p2pkh.ts). +For BCH internal helper functions compiled via `OP_DEFINE` / `OP_INVOKE`, see [`helper-functions.cash`](/examples/helper-functions.cash) and [`helper-functions.ts`](/examples/helper-functions.ts). + +For imported helper libraries, including a transitive `library -> library` helper chain, see [`imported-helper-functions.cash`](/examples/imported-helper-functions.cash), [`math-helpers.cash`](/examples/math-helpers.cash), [`parity-helpers.cash`](/examples/parity-helpers.cash), and [`imported-helper-functions.ts`](/examples/imported-helper-functions.ts). + +Both functions examples are specific to the BCH functions proposal and configure the mock provider for `BCH_2026_05`. + ## Running the examples To run the examples, clone this repository and navigate to the `examples/` directory. Since the examples depend on the SDK, be sure to run `yarn` inside the `examples/` directory, which installs all required packages. diff --git a/examples/helper-functions.cash b/examples/helper-functions.cash new file mode 100644 index 00000000..968086c5 --- /dev/null +++ b/examples/helper-functions.cash @@ -0,0 +1,24 @@ +pragma cashscript ^0.13.0; + +contract HelperFunctions() { + function spend(int value) public { + require(isPositiveEven(value)); + } + + function spendPlusOne(int value) public { + require(isPositiveEven(value + 1)); + } + + function isPositiveEven(int value) internal { + require(isPositive(value)); + require(isEven(value)); + } + + function isPositive(int value) internal { + require(value > 0); + } + + function isEven(int value) internal { + require(value % 2 == 0); + } +} diff --git a/examples/helper-functions.ts b/examples/helper-functions.ts new file mode 100644 index 00000000..8271353d --- /dev/null +++ b/examples/helper-functions.ts @@ -0,0 +1,24 @@ +import { stringify } from '@bitauth/libauth'; +import { compileFile } from 'cashc'; +import { Contract, MockNetworkProvider, randomUtxo, TransactionBuilder, VmTarget } from 'cashscript'; +import { URL } from 'url'; + +const artifact = compileFile(new URL('helper-functions.cash', import.meta.url)); +const provider = new MockNetworkProvider({ vmTarget: VmTarget.BCH_2026_05 }); +const contract = new Contract(artifact, [], { provider }); + +provider.addUtxo(contract.address, randomUtxo()); + +const [contractUtxo] = await contract.getUtxos(); +if (!contractUtxo) throw new Error('No contract UTXO found'); + +console.log('contract address:', contract.address); +console.log('public ABI functions:', artifact.abi.map((func) => func.name)); +console.log('compiler target:', artifact.compiler.target); + +const tx = await new TransactionBuilder({ provider }) + .addInput(contractUtxo, contract.unlock.spend(8n)) + .addOutput({ to: contract.address, amount: 10_000n }) + .send(); + +console.log('transaction details:', stringify(tx)); diff --git a/examples/imported-helper-functions.cash b/examples/imported-helper-functions.cash new file mode 100644 index 00000000..e7fc5cc2 --- /dev/null +++ b/examples/imported-helper-functions.cash @@ -0,0 +1,13 @@ +pragma cashscript ^0.13.0; + +import "./math-helpers.cash" as Math; + +contract ImportedHelperFunctions() { + function spend(int value) public { + require(Math.isPositiveEven(value)); + } + + function spendPlusTwo(int value) public { + require(Math.isPositiveEven(value + 2)); + } +} diff --git a/examples/imported-helper-functions.ts b/examples/imported-helper-functions.ts new file mode 100644 index 00000000..6b19450a --- /dev/null +++ b/examples/imported-helper-functions.ts @@ -0,0 +1,24 @@ +import { stringify } from '@bitauth/libauth'; +import { compileFile } from 'cashc'; +import { Contract, MockNetworkProvider, randomUtxo, TransactionBuilder, VmTarget } from 'cashscript'; +import { URL } from 'url'; + +const artifact = compileFile(new URL('imported-helper-functions.cash', import.meta.url)); +const provider = new MockNetworkProvider({ vmTarget: VmTarget.BCH_2026_05 }); +const contract = new Contract(artifact, [], { provider }); + +provider.addUtxo(contract.address, randomUtxo()); + +const [contractUtxo] = await contract.getUtxos(); +if (!contractUtxo) throw new Error('No contract UTXO found'); + +console.log('contract address:', contract.address); +console.log('public ABI functions:', artifact.abi.map((func) => func.name)); +console.log('compiler target:', artifact.compiler.target); + +const tx = await new TransactionBuilder({ provider }) + .addInput(contractUtxo, contract.unlock.spend(8n)) + .addOutput({ to: contract.address, amount: 10_000n }) + .send(); + +console.log('transaction details:', stringify(tx)); diff --git a/examples/math-helpers.cash b/examples/math-helpers.cash new file mode 100644 index 00000000..18110ce3 --- /dev/null +++ b/examples/math-helpers.cash @@ -0,0 +1,10 @@ +pragma cashscript ^0.13.0; + +import "./parity-helpers.cash" as Parity; + +library MathHelpers { + function isPositiveEven(int value) internal { + require(value > 0); + require(Parity.isEven(value)); + } +} diff --git a/examples/parity-helpers.cash b/examples/parity-helpers.cash new file mode 100644 index 00000000..2a032490 --- /dev/null +++ b/examples/parity-helpers.cash @@ -0,0 +1,7 @@ +pragma cashscript ^0.13.0; + +library ParityHelpers { + function isEven(int value) internal { + require(value % 2 == 0); + } +} diff --git a/examples/testing-suite/README.md b/examples/testing-suite/README.md index 37b83690..771a57a9 100644 --- a/examples/testing-suite/README.md +++ b/examples/testing-suite/README.md @@ -4,7 +4,7 @@ This is an example project to demonstrate how to write and compile CashScript co ## Writing a CashScript contract -We have included two simple example contracts in the `contracts/` directory that can be used as a starting point to write your own contracts. The contracts demonstrate logs, require statements and transaction signatures. +We have included example contracts in the `contracts/` directory that can be used as a starting point to write your own contracts. The `example.cash` and `transfer_with_timeout.cash` samples remain generic CashScript examples, while `internal_functions.cash` demonstrates BCH internal function calls compiled via `OP_DEFINE` / `OP_INVOKE`. ## Compiling the contracts diff --git a/examples/testing-suite/contracts/internal_functions.cash b/examples/testing-suite/contracts/internal_functions.cash new file mode 100644 index 00000000..843397c7 --- /dev/null +++ b/examples/testing-suite/contracts/internal_functions.cash @@ -0,0 +1,10 @@ +contract InternalFunctions() { + function test(int value) public { + require(validateValue(value)); + } + + function validateValue(int value) internal { + console.log(value, "internal"); + require(value == 1, "Wrong value passed"); + } +} diff --git a/examples/testing-suite/test/example.test.ts b/examples/testing-suite/test/example.test.ts index f2f2dfbd..b738ca34 100644 --- a/examples/testing-suite/test/example.test.ts +++ b/examples/testing-suite/test/example.test.ts @@ -1,9 +1,9 @@ import artifact from '../artifacts/example.artifact.js'; -import { Contract, MockNetworkProvider, TransactionBuilder, randomUtxo } from 'cashscript'; +import { Contract, MockNetworkProvider, TransactionBuilder, randomUtxo, VmTarget } from 'cashscript'; import 'cashscript/vitest'; describe('test example contract functions', () => { - const provider = new MockNetworkProvider(); + const provider = new MockNetworkProvider({ vmTarget: VmTarget.BCH_2026_05 }); const contract = new Contract(artifact, [], { provider }); // Create a contract Utxo @@ -28,4 +28,8 @@ describe('test example contract functions', () => { expect(transaction).toFailRequireWith(/Wrong value passed/); }); + it('should expose only the public test function in the ABI', () => { + expect(artifact.abi.map((func) => func.name)).toEqual(['test']); + }); + }); diff --git a/examples/testing-suite/test/internal_functions.test.ts b/examples/testing-suite/test/internal_functions.test.ts new file mode 100644 index 00000000..d249105f --- /dev/null +++ b/examples/testing-suite/test/internal_functions.test.ts @@ -0,0 +1,39 @@ +import { compileFile } from 'cashc'; +import { Contract, MockNetworkProvider, TransactionBuilder, randomUtxo, VmTarget } from 'cashscript'; +import 'cashscript/vitest'; +import { URL } from 'url'; + +const artifact = compileFile(new URL('../contracts/internal_functions.cash', import.meta.url)); + +describe('test internal function contract', () => { + const provider = new MockNetworkProvider({ vmTarget: VmTarget.BCH_2026_05 }); + const contract = new Contract(artifact, [], { provider }); + + it('should succeed when correct parameter is passed', () => { + const contractUtxo = randomUtxo(); + provider.addUtxo(contract.address, contractUtxo); + + const transaction = new TransactionBuilder({ provider }) + .addInput(contractUtxo, contract.unlock.test(1n)) + .addOutput({ to: contract.address, amount: 10000n }); + + expect(transaction).toLog(/1 internal/); + expect(transaction).not.toFailRequire(); + }); + + it('should fail require statement and log from the internal function when incorrect parameter is passed', () => { + const contractUtxo = randomUtxo(); + provider.addUtxo(contract.address, contractUtxo); + + const transaction = new TransactionBuilder({ provider }) + .addInput(contractUtxo, contract.unlock.test(0n)) + .addOutput({ to: contract.address, amount: 10000n }); + + expect(transaction).toLog(/0 internal/); + expect(transaction).toFailRequireWith(/Wrong value passed/); + }); + + it('should expose only the public test function in the ABI', () => { + expect(artifact.abi.map((func) => func.name)).toEqual(['test']); + }); +}); diff --git a/packages/cashc/src/Errors.ts b/packages/cashc/src/Errors.ts index eff20c9d..23b3e2f0 100644 --- a/packages/cashc/src/Errors.ts +++ b/packages/cashc/src/Errors.ts @@ -59,6 +59,27 @@ export class ParseError extends Error { } } +export class ImportResolutionError extends Error { + constructor(message: string) { + super(message); + this.name = this.constructor.name; + } +} + +export class InvalidImportDirectiveError extends Error { + constructor(message: string) { + super(message); + this.name = this.constructor.name; + } +} + +export class InvalidLibraryImportError extends Error { + constructor(message: string) { + super(message); + this.name = this.constructor.name; + } +} + export class UndefinedReferenceError extends CashScriptError { constructor( public node: IdentifierNode, @@ -110,6 +131,30 @@ export class EmptyContractError extends CashScriptError { } } +export class LibraryParameterError extends CashScriptError { + constructor( + public node: ContractNode, + ) { + super(node, `Library ${node.name} cannot declare constructor parameters`); + } +} + +export class LibraryPublicFunctionError extends CashScriptError { + constructor( + public node: FunctionDefinitionNode, + ) { + super(node, `Library functions cannot be public: ${node.name}`); + } +} + +export class NonSpendableCompilationError extends CashScriptError { + constructor( + public node: ContractNode, + ) { + super(node, `Library ${node.name} cannot be compiled as a spendable artifact; import it into a contract instead`); + } +} + export class EmptyFunctionError extends CashScriptError { constructor( public node: FunctionDefinitionNode, @@ -244,6 +289,40 @@ export class ArrayElementError extends CashScriptError { } } +export class InvokedFunctionSignatureCheckError extends CashScriptError { + constructor( + public node: FunctionCallNode, + ) { + super(node, `Invoked functions cannot use '${node.identifier.name}'`); + } +} + +export class InvokedFunctionContractParameterReferenceError extends CashScriptError { + constructor( + public node: IdentifierNode, + ) { + super(node, `Invoked functions cannot reference contract parameter '${node.name}'`); + } +} + +export class CircularFunctionDependencyError extends CashScriptError { + constructor( + public node: FunctionDefinitionNode, + ) { + super(node, `Circular dependency detected for function '${node.name}'`); + } +} + +export class UnsupportedTargetError extends Error { + constructor( + public actual: string, + public required: string, + ) { + super(`Compiler target ${actual} is incompatible with this contract; at least ${required} is required`); + this.name = this.constructor.name; + } +} + export class IndexOutOfBoundsError extends CashScriptError { constructor( node: TupleIndexOpNode | BinaryOpNode | SliceNode, diff --git a/packages/cashc/src/artifact/Artifact.ts b/packages/cashc/src/artifact/Artifact.ts index 7716cd30..f67bac12 100644 --- a/packages/cashc/src/artifact/Artifact.ts +++ b/packages/cashc/src/artifact/Artifact.ts @@ -1,8 +1,44 @@ import { - Artifact, CompilerOptions, DebugInformation, Script, scriptToAsm, + Artifact, CompilerOptions, DebugInformation, Script, VmTarget, scriptToAsm, } from '@cashscript/utils'; import { version } from '../index.js'; import { Ast } from '../ast/AST.js'; +import { UnsupportedTargetError } from '../Errors.js'; +import { getPublicFunctions } from '../utils.js'; + +function normaliseCompilerOptions(compilerOptions: CompilerOptions): CompilerOptions { + return Object.fromEntries( + Object.entries(compilerOptions).filter(([key, value]) => { + if (value === undefined) return false; + if (key === 'target') return false; + return true; + }), + ); +} + +function inferCompilerTarget(bytecode: string): VmTarget | undefined { + const requiresBch2026 = [ + 'OP_DEFINE', + 'OP_INVOKE', + ].some((opcode) => bytecode.includes(opcode)); + + return requiresBch2026 ? 'BCH_2026_05' : undefined; +} + +function resolveCompilerTarget(bytecode: string, compilerOptions: CompilerOptions): VmTarget | undefined { + const inferredTarget = inferCompilerTarget(bytecode); + + if ( + inferredTarget === 'BCH_2026_05' + && compilerOptions.target !== undefined + && compilerOptions.target !== 'BCH_2026_05' + && compilerOptions.target !== 'BCH_SPEC' + ) { + throw new UnsupportedTargetError(compilerOptions.target, inferredTarget); + } + + return compilerOptions.target ?? inferredTarget; +} export function generateArtifact( ast: Ast, @@ -16,7 +52,7 @@ export function generateArtifact( const constructorInputs = contract.parameters .map((parameter) => ({ name: parameter.name, type: parameter.type.toString() })); - const abi = contract.functions.map((func) => ({ + const abi = getPublicFunctions(contract.functions).map((func) => ({ name: func.name, inputs: func.parameters.map((parameter) => ({ name: parameter.name, @@ -25,6 +61,7 @@ export function generateArtifact( })); const bytecode = scriptToAsm(script); + const compilerTarget = resolveCompilerTarget(bytecode, compilerOptions); return { contractName: contract.name, @@ -36,7 +73,8 @@ export function generateArtifact( compiler: { name: 'cashc', version, - options: compilerOptions, + ...(compilerTarget !== undefined ? { target: compilerTarget } : {}), + options: normaliseCompilerOptions(compilerOptions), }, updatedAt: new Date().toISOString(), }; diff --git a/packages/cashc/src/ast/AST.ts b/packages/cashc/src/ast/AST.ts index a3806e29..bc2504f4 100644 --- a/packages/cashc/src/ast/AST.ts +++ b/packages/cashc/src/ast/AST.ts @@ -1,5 +1,5 @@ import { Type, PrimitiveType, BytesType } from '@cashscript/utils'; -import { TimeOp } from './Globals.js'; +import { FunctionVisibility, TimeOp } from './Globals.js'; import AstVisitor from './AstVisitor.js'; import { BinaryOperator, NullaryOperator, UnaryOperator } from './Operator.js'; import { Location } from './Location.js'; @@ -21,6 +21,8 @@ export interface Typed { type: Type; } +export type ContainerKind = 'contract' | 'library'; + export class SourceFileNode extends Node { constructor( public contract: ContractNode, @@ -35,11 +37,14 @@ export class SourceFileNode extends Node { export class ContractNode extends Node implements Named { symbolTable?: SymbolTable; + sourceCode?: string; + sourceFile?: string; constructor( public name: string, public parameters: ParameterNode[], public functions: FunctionDefinitionNode[], + public kind: ContainerKind = 'contract', ) { super(); } @@ -52,11 +57,15 @@ export class ContractNode extends Node implements Named { export class FunctionDefinitionNode extends Node implements Named { symbolTable?: SymbolTable; opRolls: Map = new Map(); + calledFunctions: Set = new Set(); + sourceCode?: string; + sourceFile?: string; constructor( public name: string, public parameters: ParameterNode[], public body: BlockNode, + public visibility: FunctionVisibility = FunctionVisibility.PUBLIC, ) { super(); } diff --git a/packages/cashc/src/ast/AstBuilder.ts b/packages/cashc/src/ast/AstBuilder.ts index f0585716..26931ebc 100644 --- a/packages/cashc/src/ast/AstBuilder.ts +++ b/packages/cashc/src/ast/AstBuilder.ts @@ -41,6 +41,7 @@ import { import { UnaryOperator, BinaryOperator, NullaryOperator } from './Operator.js'; import type { ContractDefinitionContext, + LibraryDefinitionContext, FunctionDefinitionContext, VariableDefinitionContext, TupleAssignmentContext, @@ -51,6 +52,7 @@ import type { CastContext, LiteralContext, SourceFileContext, + TopLevelDefinitionContext, BlockContext, TimeOpStatementContext, ArrayContext, @@ -83,6 +85,7 @@ import CashScriptVisitor from '../grammar/CashScriptVisitor.js'; import { Location } from './Location.js'; import { NumberUnit, + FunctionVisibility, TimeOp, } from './Globals.js'; import { getPragmaName, PragmaName, getVersionOpFromCtx } from './Pragma.js'; @@ -92,7 +95,12 @@ import { ParseError, VersionError } from '../Errors.js'; export default class AstBuilder extends ParseTreeVisitor implements CashScriptVisitor { - constructor(private tree: ParseTree) { + private functionVisibilityIndex = 0; + + constructor( + private tree: ParseTree, + private functionVisibilities: FunctionVisibility[] = [], + ) { super(); } @@ -109,12 +117,20 @@ export default class AstBuilder this.processPragma(pragma); }); - const contract = this.visit(ctx.contractDefinition()) as ContractNode; + const contract = this.visit(ctx.topLevelDefinition()) as ContractNode; const sourceFileNode = new SourceFileNode(contract); sourceFileNode.location = Location.fromCtx(ctx); return sourceFileNode; } + visitTopLevelDefinition(ctx: TopLevelDefinitionContext): ContractNode { + if (ctx.contractDefinition()) { + return this.visit(ctx.contractDefinition()) as ContractNode; + } + + return this.visit(ctx.libraryDefinition()!) as ContractNode; + } + processPragma(ctx: PragmaDirectiveContext): void { const pragmaName = getPragmaName(ctx.pragmaName().getText()); if (pragmaName !== PragmaName.CASHSCRIPT) throw new Error(); // Shouldn't happen @@ -135,7 +151,15 @@ export default class AstBuilder const name = ctx.Identifier().getText(); const parameters = ctx.parameterList().parameter_list().map((p) => this.visit(p) as ParameterNode); const functions = ctx.functionDefinition_list().map((f) => this.visit(f) as FunctionDefinitionNode); - const contract = new ContractNode(name, parameters, functions); + const contract = new ContractNode(name, parameters, functions, 'contract'); + contract.location = Location.fromCtx(ctx); + return contract; + } + + visitLibraryDefinition(ctx: LibraryDefinitionContext): ContractNode { + const name = ctx.Identifier().getText(); + const functions = ctx.functionDefinition_list().map((f) => this.visit(f) as FunctionDefinitionNode); + const contract = new ContractNode(name, [], functions, 'library'); contract.location = Location.fromCtx(ctx); return contract; } @@ -146,8 +170,10 @@ export default class AstBuilder const statements = ctx.statement_list().map((s) => this.visit(s) as StatementNode); const block = new BlockNode(statements); block.location = Location.fromCtx(ctx); + const visibility = this.functionVisibilities[this.functionVisibilityIndex] ?? FunctionVisibility.PUBLIC; + this.functionVisibilityIndex += 1; - const functionDefinition = new FunctionDefinitionNode(name, parameters, block); + const functionDefinition = new FunctionDefinitionNode(name, parameters, block, visibility); functionDefinition.location = Location.fromCtx(ctx); return functionDefinition; } diff --git a/packages/cashc/src/ast/Globals.ts b/packages/cashc/src/ast/Globals.ts index cc1273d7..99a83630 100644 --- a/packages/cashc/src/ast/Globals.ts +++ b/packages/cashc/src/ast/Globals.ts @@ -46,6 +46,11 @@ export enum Modifier { CONSTANT = 'constant', } +export enum FunctionVisibility { + PUBLIC = 'public', + INTERNAL = 'internal', +} + export const GLOBAL_SYMBOL_TABLE = new SymbolTable(); // Classes diff --git a/packages/cashc/src/ast/Location.ts b/packages/cashc/src/ast/Location.ts index a3054606..90b2afd7 100644 --- a/packages/cashc/src/ast/Location.ts +++ b/packages/cashc/src/ast/Location.ts @@ -2,7 +2,7 @@ import type { ParserRuleContext, Token } from 'antlr4'; import { LocationI } from '@cashscript/utils'; export class Location implements LocationI { - constructor(public start: Point, public end: Point) {} + constructor(public start: Point, public end: Point, public sourceFile?: string) {} static fromCtx(ctx: ParserRuleContext): Location { const stop = ctx.stop?.text ? ctx.stop : ctx.start; @@ -27,7 +27,7 @@ export class Location implements LocationI { const start = new Point(object.start.line, object.start.column); const end = new Point(object.end.line, object.end.column); - return new Location(start, end); + return new Location(start, end, object.sourceFile); } text(code: string): string { diff --git a/packages/cashc/src/ast/SymbolTable.ts b/packages/cashc/src/ast/SymbolTable.ts index fcfada94..77046501 100644 --- a/packages/cashc/src/ast/SymbolTable.ts +++ b/packages/cashc/src/ast/SymbolTable.ts @@ -28,6 +28,10 @@ export class Symbol { return new Symbol(name, type, SymbolType.FUNCTION, undefined, parameters); } + static definedFunction(name: string, type: Type, definition: Node, parameters: Type[]): Symbol { + return new Symbol(name, type, SymbolType.FUNCTION, definition, parameters); + } + static class(name: string, type: Type, parameters: Type[]): Symbol { return new Symbol(name, type, SymbolType.CLASS, undefined, parameters); } @@ -73,6 +77,6 @@ export class SymbolTable { unusedSymbols(): Symbol[] { return Array.from(this.symbols) .map((e) => e[1]) - .filter((s) => s.references.length === 0); + .filter((s) => s.symbolType === SymbolType.VARIABLE && s.references.length === 0); } } diff --git a/packages/cashc/src/cashc-cli.ts b/packages/cashc/src/cashc-cli.ts index 9d35ffd0..38e3380b 100644 --- a/packages/cashc/src/cashc-cli.ts +++ b/packages/cashc/src/cashc-cli.ts @@ -26,6 +26,10 @@ program .option('-c, --opcount', 'Display the number of opcodes in the compiled bytecode.') .option('-s, --size', 'Display the size in bytes of the compiled bytecode.') .option('-S, --skip-enforce-function-parameter-types', 'Do not enforce function parameter types.') + .addOption( + new Option('-t, --target ', 'Record a required VM target in the artifact metadata.') + .choices(['BCH_2023_05', 'BCH_2025_05', 'BCH_2026_05', 'BCH_SPEC']), + ) .addOption( new Option('-f, --format ', 'Specify the format of the output.') .choices(['json', 'ts']) @@ -51,6 +55,7 @@ function run(): void { const compilerOptions: CompilerOptions = { enforceFunctionParameterTypes: !opts.skipEnforceFunctionParameterTypes, + ...(opts.target ? { target: opts.target } : {}), }; try { diff --git a/packages/cashc/src/compiler.ts b/packages/cashc/src/compiler.ts index ebc49c06..25022ffb 100644 --- a/packages/cashc/src/compiler.ts +++ b/packages/cashc/src/compiler.ts @@ -1,10 +1,12 @@ -import { CharStream, CommonTokenStream } from 'antlr4'; +import { CharStream, CommonTokenStream, Token } from 'antlr4'; import { binToHex } from '@bitauth/libauth'; import { Artifact, CompilerOptions, generateSourceMap, generateSourceTags, optimiseBytecode, optimiseBytecodeOld, scriptToAsm, scriptToBytecode, sourceMapToLocationData } from '@cashscript/utils'; import fs, { PathLike } from 'fs'; +import { fileURLToPath } from 'url'; import { generateArtifact } from './artifact/Artifact.js'; -import { Ast } from './ast/AST.js'; +import { Ast, ContractNode, FunctionDefinitionNode, Node } from './ast/AST.js'; import AstBuilder from './ast/AstBuilder.js'; +import AstTraversal from './ast/AstTraversal.js'; import ThrowingErrorListener from './ast/ThrowingErrorListener.js'; import GenerateTargetTraversal from './generation/GenerateTargetTraversal.js'; import CashScriptLexer from './grammar/CashScriptLexer.js'; @@ -12,25 +14,60 @@ import CashScriptParser from './grammar/CashScriptParser.js'; import SymbolTableTraversal from './semantic/SymbolTableTraversal.js'; import TypeCheckTraversal from './semantic/TypeCheckTraversal.js'; import EnsureFinalRequireTraversal from './semantic/EnsureFinalRequireTraversal.js'; +import EnsureInvokedFunctionsSafeTraversal from './semantic/EnsureInvokedFunctionsSafeTraversal.js'; +import EnsureContainerSemanticsTraversal from './semantic/EnsureContainerSemanticsTraversal.js'; +import { FunctionVisibility } from './ast/Globals.js'; +import { Point } from './ast/Location.js'; +import { NonSpendableCompilationError, ParseError } from './Errors.js'; +import { ImportResolver, ImportedFunctionProvenance, preprocessImports } from './imports.js'; export const DEFAULT_COMPILER_OPTIONS: CompilerOptions = { enforceFunctionParameterTypes: true, }; -export function compileString(code: string, compilerOptions: CompilerOptions = {}): Artifact { - const mergedCompilerOptions = { ...DEFAULT_COMPILER_OPTIONS, ...compilerOptions }; +export interface CompileOptions extends CompilerOptions { + sourcePath?: string; + resolveImport?: ImportResolver; +} + +type ContainerKind = 'contract' | 'library'; + +type PreprocessedVisibilityResult = { + code: string; + containerKind: ContainerKind; + functionVisibilities: FunctionVisibility[]; + omittedPublicFunctions: Array<{ name: string; line: number; column: number }>; +}; + +export function compileString(code: string, compilerOptions: CompileOptions = {}): Artifact { + const { sourcePath, resolveImport, ...serialisableCompilerOptions } = compilerOptions; + const mergedCompilerOptions = { ...DEFAULT_COMPILER_OPTIONS, ...serialisableCompilerOptions }; + const importedCode = preprocessImports(code, { sourcePath, resolveImport }); + const preprocessed = preprocessFunctionVisibility(importedCode.code, mergedCompilerOptions); + emitVisibilityWarnings(preprocessed.omittedPublicFunctions); // Lexing + parsing - let ast = parseCode(code); + let ast = parseCodeFromPreprocessed(preprocessed); + ast = applySourceProvenance(ast, importedCode.functionProvenance, { + source: code, + ...(sourcePath ? { sourceFile: normaliseSourcePath(sourcePath) } : {}), + }); // Semantic analysis ast = ast.accept(new SymbolTableTraversal()) as Ast; ast = ast.accept(new TypeCheckTraversal()) as Ast; + ast = ast.accept(new EnsureContainerSemanticsTraversal()) as Ast; ast = ast.accept(new EnsureFinalRequireTraversal()) as Ast; + ast = ast.accept(new EnsureInvokedFunctionsSafeTraversal()) as Ast; + + if (ast.contract.kind === 'library') { + throw new NonSpendableCompilationError(ast.contract); + } // Code generation const traversal = new GenerateTargetTraversal(mergedCompilerOptions); ast = ast.accept(traversal) as Ast; + assertDistinctHelperFrameBytecode(traversal); const constructorParamLength = ast.contract.parameters.length; @@ -53,25 +90,64 @@ export function compileString(code: string, compilerOptions: CompilerOptions = { // Attach debug information const sourceTags = generateSourceTags(optimisationResult.sourceTags); + const rootFrameBytecode = binToHex(scriptToBytecode(optimisationResult.script)); const debug = { - bytecode: binToHex(scriptToBytecode(optimisationResult.script)), + bytecode: rootFrameBytecode, sourceMap: generateSourceMap(optimisationResult.locationData), - logs: optimisationResult.logs, - requires: optimisationResult.requires, + logs: optimisationResult.logs.map((log) => ({ + ...log, + frameBytecode: log.frameBytecode ?? rootFrameBytecode, + frameId: log.frameId ?? '__root__', + sourceFile: log.sourceFile ?? ast.contract.sourceFile, + data: log.data.map((entry) => ( + typeof entry === 'string' + ? entry + : { + ...entry, + frameBytecode: entry.frameBytecode ?? log.frameBytecode ?? rootFrameBytecode, + frameId: entry.frameId ?? log.frameId ?? '__root__', + } + )), + })), + requires: optimisationResult.requires.map((require) => ({ + ...require, + frameBytecode: require.frameBytecode ?? rootFrameBytecode, + frameId: require.frameId ?? '__root__', + sourceFile: require.sourceFile ?? ast.contract.sourceFile, + })), + frames: [ + { + id: '__root__', + bytecode: rootFrameBytecode, + sourceMap: generateSourceMap(optimisationResult.locationData), + source: ast.contract.sourceCode ?? code, + ...(ast.contract.sourceFile ? { sourceFile: ast.contract.sourceFile } : {}), + }, + ...traversal.frames, + ], ...(sourceTags ? { sourceTags } : {}), }; - return generateArtifact(ast, optimisationResult.script, code, debug, mergedCompilerOptions); + return generateArtifact(ast, optimisationResult.script, importedCode.code, debug, mergedCompilerOptions); } -export function compileFile(codeFile: PathLike, compilerOptions: CompilerOptions = {}): Artifact { +export function compileFile(codeFile: PathLike, compilerOptions: CompileOptions = {}): Artifact { const code = fs.readFileSync(codeFile, { encoding: 'utf-8' }); - return compileString(code, compilerOptions); + return compileString(code, { ...compilerOptions, sourcePath: normaliseSourcePath(codeFile) }); } -export function parseCode(code: string): Ast { +export function parseCode(code: string, compilerOptions: Pick = {}): Ast { + const importedCode = preprocessImports(code, compilerOptions); + const preprocessed = preprocessFunctionVisibility(importedCode.code); + return applySourceProvenance(parseCodeFromPreprocessed(preprocessed), importedCode.functionProvenance, { + source: code, + ...(compilerOptions.sourcePath ? { sourceFile: normaliseSourcePath(compilerOptions.sourcePath) } : {}), + }); +} + +function parseCodeFromPreprocessed(preprocessed: PreprocessedVisibilityResult): Ast { // Lexing (throwing on errors) - const inputStream = new CharStream(code); + const inputStream = new CharStream(preprocessed.code); const lexer = new CashScriptLexer(inputStream); lexer.removeErrorListeners(); lexer.addErrorListener(ThrowingErrorListener.INSTANCE); @@ -84,7 +160,225 @@ export function parseCode(code: string): Ast { const parseTree = parser.sourceFile(); // AST building - const ast = new AstBuilder(parseTree).build() as Ast; + const ast = new AstBuilder(parseTree, preprocessed.functionVisibilities).build() as Ast; return ast; } + +function preprocessFunctionVisibility(code: string, compilerOptions: CompilerOptions = {}): PreprocessedVisibilityResult { + const containerKind = getTopLevelContainerKind(code); + const inputStream = new CharStream(code); + const lexer = new CashScriptLexer(inputStream); + const tokenStream = new CommonTokenStream(lexer); + tokenStream.fill(); + + const visibleTokens = tokenStream.tokens.filter((token) => token.channel === 0 && token.type !== -1); + const mutableCode = code.split(''); + const functionVisibilities: FunctionVisibility[] = []; + const omittedPublicFunctions: Array<{ name: string; line: number; column: number }> = []; + + for (let i = 0; i < visibleTokens.length; i += 1) { + if (visibleTokens[i].text !== 'function') continue; + + let cursor = i + 1; + if (!visibleTokens[cursor] || !visibleTokens[cursor + 1] || visibleTokens[cursor + 1].text !== '(') continue; + const functionNameToken = visibleTokens[cursor]; + + cursor += 2; + let depth = 1; + while (cursor < visibleTokens.length && depth > 0) { + if (visibleTokens[cursor].text === '(') depth += 1; + if (visibleTokens[cursor].text === ')') depth -= 1; + cursor += 1; + } + + const visibilityToken = visibleTokens[cursor]; + if (visibilityToken?.text === FunctionVisibility.INTERNAL || visibilityToken?.text === FunctionVisibility.PUBLIC) { + if (containerKind === 'library' && visibilityToken.text === FunctionVisibility.PUBLIC) { + throw new ParseError( + `Library function '${functionNameToken.text}' must declare internal visibility`, + new Point(visibilityToken.line, visibilityToken.column), + ); + } + + functionVisibilities.push(visibilityToken.text as FunctionVisibility); + for (let index = visibilityToken.start; index <= visibilityToken.stop; index += 1) { + mutableCode[index] = ' '; + } + continue; + } + + if (containerKind === 'library') { + throw new ParseError( + `Library function '${functionNameToken.text}' must declare internal visibility`, + new Point(functionNameToken.line, functionNameToken.column), + ); + } + + if (compilerOptions.requireExplicitFunctionVisibility) { + throw new ParseError( + `Function '${functionNameToken.text}' must declare explicit visibility (public or internal)`, + new Point(functionNameToken.line, functionNameToken.column), + ); + } + + const defaultVisibility = FunctionVisibility.PUBLIC; + functionVisibilities.push(defaultVisibility); + if (defaultVisibility === FunctionVisibility.PUBLIC) { + omittedPublicFunctions.push({ + name: functionNameToken.text!, + line: functionNameToken.line, + column: functionNameToken.column, + }); + } + } + + return { code: mutableCode.join(''), containerKind, functionVisibilities, omittedPublicFunctions }; +} + +function getTopLevelContainerKind(code: string): ContainerKind { + const tokens = getVisibleTokens(code); + let cursor = 0; + + while (cursor < tokens.length && tokens[cursor].text === 'pragma') { + cursor = advanceToSemicolon(tokens, cursor + 1); + } + + while (cursor < tokens.length && tokens[cursor].text === 'import') { + cursor = advanceToSemicolon(tokens, cursor + 1); + } + + const rootToken = tokens[cursor]; + if (!rootToken) { + throw new ParseError('Expected a root contract or library definition'); + } + + if (rootToken.text === 'contract' || rootToken.text === 'library') { + return rootToken.text; + } + + throw new ParseError(`Expected a root contract or library definition, found '${rootToken.text}'`); +} + +function emitVisibilityWarnings(omittedPublicFunctions: Array<{ name: string; line: number; column: number }>): void { + if (omittedPublicFunctions.length === 0) return; + + const summary = omittedPublicFunctions + .map(({ name, line, column }) => `${name} (Line ${line}, Column ${column})`) + .join(', '); + + console.warn( + `Warning: ${omittedPublicFunctions.length} function(s) omit visibility and default to public: ${summary}.`, + ); +} + +class SourceProvenanceTraversal extends AstTraversal { + private currentSource = this.rootSource; + private currentLineDelta = 0; + private currentColumnDelta = 0; + + constructor( + private functionProvenance: Map, + private rootSource: { source: string; sourceFile?: string }, + ) { + super(); + } + + visit(node: Node): Node { + if (node.location) { + node.location.start.line += this.currentLineDelta; + node.location.end.line += this.currentLineDelta; + node.location.start.column += this.currentColumnDelta; + node.location.end.column += this.currentColumnDelta; + node.location.sourceFile = this.currentSource.sourceFile; + } + + return super.visit(node); + } + + visitContract(node: ContractNode): Node { + node.sourceCode = this.rootSource.source; + node.sourceFile = this.rootSource.sourceFile; + return super.visitContract(node); + } + + visitFunctionDefinition(node: FunctionDefinitionNode): Node { + const previousSource = this.currentSource; + const previousLineDelta = this.currentLineDelta; + const previousColumnDelta = this.currentColumnDelta; + const provenance = this.functionProvenance.get(node.name); + + this.currentSource = provenance + ? { source: provenance.source, sourceFile: provenance.sourceFile } + : this.rootSource; + this.currentLineDelta = provenance ? provenance.originalStartLine - node.location.start.line : 0; + this.currentColumnDelta = provenance ? provenance.originalStartColumn - provenance.generatedStartColumn : 0; + node.sourceCode = this.currentSource.source; + node.sourceFile = this.currentSource.sourceFile; + + const result = super.visitFunctionDefinition(node); + + this.currentSource = previousSource; + this.currentLineDelta = previousLineDelta; + this.currentColumnDelta = previousColumnDelta; + return result; + } +} + +function applySourceProvenance( + ast: Ast, + functionProvenance: ImportedFunctionProvenance[], + rootSource: { source: string; sourceFile?: string }, +): Ast { + const provenanceMap = new Map(functionProvenance.map((entry) => [entry.mangledName, entry])); + return ast.accept(new SourceProvenanceTraversal(provenanceMap, rootSource)) as Ast; +} + +function assertDistinctHelperFrameBytecode(traversal: GenerateTargetTraversal): void { + const frameIdsByBytecode = new Map(); + + traversal.frames.forEach((frame) => { + const previousFrameId = frameIdsByBytecode.get(frame.bytecode); + if (previousFrameId && previousFrameId !== frame.id) { + throw new Error( + `Cannot compile helper functions '${previousFrameId}' and '${frame.id}' because they produce identical helper bytecode, which would make runtime debugging ambiguous.`, + ); + } + + frameIdsByBytecode.set(frame.bytecode, frame.id); + }); +} + +function getVisibleTokens(code: string): Token[] { + const inputStream = new CharStream(code); + const lexer = new CashScriptLexer(inputStream); + const tokenStream = new CommonTokenStream(lexer); + tokenStream.fill(); + + return tokenStream.tokens.filter((token) => token.channel === 0 && token.type !== -1); +} + +function advanceToSemicolon(tokens: ReturnType, cursor: number): number { + while (cursor < tokens.length && tokens[cursor].text !== ';') { + cursor += 1; + } + + if (tokens[cursor]?.text !== ';') { + throw new ParseError('Expected semicolon while parsing pragmas'); + } + + return cursor + 1; +} + +function normaliseSourcePath(codeFile: PathLike): string { + if (codeFile instanceof URL) { + return fileURLToPath(codeFile); + } + + const sourcePath = String(codeFile); + if (sourcePath.startsWith('file://')) { + return fileURLToPath(sourcePath); + } + + return sourcePath; +} diff --git a/packages/cashc/src/generation/GenerateTargetTraversal.ts b/packages/cashc/src/generation/GenerateTargetTraversal.ts index 500ac980..5588bb44 100644 --- a/packages/cashc/src/generation/GenerateTargetTraversal.ts +++ b/packages/cashc/src/generation/GenerateTargetTraversal.ts @@ -1,4 +1,4 @@ -import { hexToBin } from '@bitauth/libauth'; +import { binToHex, hexToBin } from '@bitauth/libauth'; import { asmToScript, encodeBool, @@ -9,6 +9,7 @@ import { PrimitiveType, Script, scriptToAsm, + scriptToBytecode, generateSourceMap, FullLocationData, LogEntry, @@ -65,7 +66,7 @@ import { compileTimeOp, compileUnaryOp, } from './utils.js'; -import { isNumericType } from '../utils.js'; +import { getInvokedFunctionClosure, getPublicFunctions, isNumericType } from '../utils.js'; export default class GenerateTargetTraversalWithLocation extends AstTraversal { private locationData: FullLocationData = []; // detailed location data needed for sourcemap creation @@ -74,12 +75,14 @@ export default class GenerateTargetTraversalWithLocation extends AstTraversal { stack: string[] = []; consoleLogs: LogEntry[] = []; requires: RequireStatement[] = []; + frames: Array<{ id: string; bytecode: string; sourceMap: string; source: string; sourceFile?: string }> = []; sourceTags: SourceTagEntry[] = []; finalStackUsage: Record = {}; private scopeDepth = 0; private currentFunction: FunctionDefinitionNode; private constructorParameterCount: number; + private functionIndices = new Map(); constructor(private compilerOptions: CompilerOptions) { super(); @@ -145,22 +148,30 @@ export default class GenerateTargetTraversalWithLocation extends AstTraversal { visitContract(node: ContractNode): Node { node.parameters = this.visitList(node.parameters) as ParameterNode[]; + const publicFunctions = getPublicFunctions(node.functions); // Keep track of constructor parameter count for instructor pointer calculation this.constructorParameterCount = node.parameters.length; - if (node.functions.length === 1) { - node.functions = this.visitList(node.functions) as FunctionDefinitionNode[]; + const invokedFunctions = getInvokedFunctionClosure(node.functions); + if (invokedFunctions.size > 0) { + const functionDefinitions = node.functions.filter((func) => invokedFunctions.has(func.name)); + this.functionIndices = new Map(functionDefinitions.map((func, index) => [func.name, index])); + this.emitFunctionDefinitions(functionDefinitions); + } + + if (publicFunctions.length === 1) { + publicFunctions[0] = this.emitPublicFunctionDispatch(publicFunctions[0]) as FunctionDefinitionNode; } else { this.pushToStack('$$', true); - node.functions = node.functions.map((f, i) => { + publicFunctions.forEach((f, i) => { const locationData = { location: f.location, positionHint: PositionHint.START }; const stackCopy = [...this.stack]; const selectorIndex = this.getStackIndex('$$'); this.emit(encodeInt(BigInt(selectorIndex)), locationData); - if (i === node.functions.length - 1) { + if (i === publicFunctions.length - 1) { this.emit(Op.OP_ROLL, locationData); this.removeFromStack(selectorIndex); } else { @@ -171,23 +182,22 @@ export default class GenerateTargetTraversalWithLocation extends AstTraversal { this.emit(encodeInt(BigInt(i)), locationData); this.emit(Op.OP_NUMEQUAL, locationData); - if (i < node.functions.length - 1) { + if (i < publicFunctions.length - 1) { this.emit(Op.OP_IF, locationData); } else { this.emit(Op.OP_VERIFY, locationData); } - f = this.visit(f) as FunctionDefinitionNode; + this.emitPublicFunctionDispatch(f); - if (i < node.functions.length - 1) { + if (i < publicFunctions.length - 1) { this.emit(Op.OP_ELSE, { ...locationData, positionHint: PositionHint.END }); } this.stack = [...stackCopy]; - return f; }); - for (let i = 0; i < node.functions.length - 1; i += 1) { + for (let i = 0; i < publicFunctions.length - 1; i += 1) { this.emit(Op.OP_ENDIF, { location: node.location, positionHint: PositionHint.END }); } } @@ -195,6 +205,28 @@ export default class GenerateTargetTraversalWithLocation extends AstTraversal { return node; } + private emitPublicFunctionDispatch(node: FunctionDefinitionNode): Node { + if (this.functionIndices.has(node.name)) { + node.parameters = this.visitList(node.parameters) as ParameterNode[]; + + if (this.compilerOptions.enforceFunctionParameterTypes) { + this.enforceFunctionParameterTypes(node); + } + + this.emit( + encodeInt(BigInt(this.functionIndices.get(node.name)!)), + { location: node.location, positionHint: PositionHint.START }, + ); + this.pushToStack('(value)'); + this.emit(Op.OP_INVOKE, { location: node.location, positionHint: PositionHint.END }); + this.popFromStack(node.parameters.length + 1); + this.pushToStack('(value)'); + return node; + } + + return this.visit(node); + } + visitFunctionDefinition(node: FunctionDefinitionNode): Node { this.currentFunction = node; @@ -212,6 +244,70 @@ export default class GenerateTargetTraversalWithLocation extends AstTraversal { return node; } + private emitFunctionDefinitions(functions: FunctionDefinitionNode[]): void { + functions.forEach((func) => { + const locationData = { location: func.location, positionHint: PositionHint.START }; + const functionBytecode = this.generateFunctionBytecode(func); + + this.emit(functionBytecode, locationData); + this.pushToStack('(value)'); + this.emit(encodeInt(BigInt(this.functionIndices.get(func.name)!)), locationData); + this.pushToStack('(value)'); + this.emit(Op.OP_DEFINE, { location: func.location, positionHint: PositionHint.END }); + this.popFromStack(2); + }); + } + + private generateFunctionBytecode(node: FunctionDefinitionNode): Uint8Array { + const traversal = new GenerateTargetTraversalWithLocation(this.compilerOptions); + traversal.functionIndices = new Map(this.functionIndices); + traversal.constructorParameterCount = 0; + node.accept(traversal); + traversal.output = asmToScript(scriptToAsm(traversal.output)); + const frameBytecode = scriptToBytecode(traversal.output); + const frameBytecodeHex = binToHex(frameBytecode); + const frameId = `fn:${this.functionIndices.get(node.name)!}`; + traversal.annotateFrameDebugMetadata(frameBytecodeHex, frameId, node.sourceFile); + this.frames.push(...traversal.frames); + this.frames.push({ + id: frameId, + bytecode: frameBytecodeHex, + sourceMap: generateSourceMap(traversal.locationData), + source: node.sourceCode ?? '', + ...(node.sourceFile ? { sourceFile: node.sourceFile } : {}), + }); + this.consoleLogs.push(...traversal.consoleLogs); + this.requires.push(...traversal.requires); + return frameBytecode; + } + + annotateFrameDebugMetadata(frameBytecode: string, frameId: string, sourceFile?: string): void { + this.finalStackUsage = Object.fromEntries( + Object.entries(this.finalStackUsage).map(([name, usage]) => [name, { + ...usage, + frameBytecode, + frameId, + }]), + ); + + this.consoleLogs = this.consoleLogs.map((log) => ({ + ...log, + frameBytecode, + frameId, + ...(sourceFile ? { sourceFile } : {}), + data: log.data.map((entry) => ( + typeof entry === 'string' ? entry : { ...entry, frameBytecode, frameId } + )), + })); + + this.requires = this.requires.map((require) => ({ + ...require, + frameBytecode, + frameId, + ...(sourceFile ? { sourceFile } : {}), + })); + } + removeFinalVerifyFromFunction(functionBodyNode: Node): void { // After EnsureFinalRequireTraversal, we know that the final opcodes are either // "OP_VERIFY", "OP_CHECK{LOCKTIME|SEQUENCE}VERIFY OP_DROP", "OP_ENDIF" or "OP_UNTIL" @@ -354,6 +450,8 @@ export default class GenerateTargetTraversalWithLocation extends AstTraversal { ip: this.getMostRecentInstructionPointer() - 1, line: node.location.start.line, message: node.message, + location: node.location, + ...(this.currentFunction.sourceFile ? { sourceFile: this.currentFunction.sourceFile } : {}), }); this.popFromStack(); @@ -369,6 +467,8 @@ export default class GenerateTargetTraversalWithLocation extends AstTraversal { ip: this.getMostRecentInstructionPointer(), line: node.location.start.line, message: node.message, + location: node.location, + ...(this.currentFunction.sourceFile ? { sourceFile: this.currentFunction.sourceFile } : {}), }); this.popFromStack(); @@ -402,7 +502,12 @@ export default class GenerateTargetTraversalWithLocation extends AstTraversal { return parameter.toString(); }); - this.consoleLogs.push({ ip, line, data }); + this.consoleLogs.push({ + ip, + line, + data, + ...(this.currentFunction.sourceFile ? { sourceFile: this.currentFunction.sourceFile } : {}), + }); return node; } @@ -550,6 +655,18 @@ export default class GenerateTargetTraversalWithLocation extends AstTraversal { node.parameters = this.visitList(node.parameters); + if (node.identifier.definition?.definition instanceof FunctionDefinitionNode) { + this.emit( + encodeInt(BigInt(this.functionIndices.get(node.identifier.name)!)), + { location: node.location, positionHint: PositionHint.START }, + ); + this.pushToStack('(value)'); + this.emit(Op.OP_INVOKE, { location: node.location, positionHint: PositionHint.END }); + this.popFromStack(node.parameters.length + 1); + this.pushToStack('(value)'); + return node; + } + this.emit( compileGlobalFunction(node.identifier.name as GlobalFunction), { location: node.location, positionHint: PositionHint.END }, diff --git a/packages/cashc/src/grammar/CashScript.g4 b/packages/cashc/src/grammar/CashScript.g4 index d970a06e..46dcd6a8 100644 --- a/packages/cashc/src/grammar/CashScript.g4 +++ b/packages/cashc/src/grammar/CashScript.g4 @@ -1,7 +1,12 @@ grammar CashScript; sourceFile - : pragmaDirective* contractDefinition EOF + : pragmaDirective* topLevelDefinition EOF + ; + +topLevelDefinition + : contractDefinition + | libraryDefinition ; pragmaDirective @@ -28,6 +33,10 @@ contractDefinition : 'contract' Identifier parameterList '{' functionDefinition* '}' ; +libraryDefinition + : 'library' Identifier '{' functionDefinition* '}' + ; + functionDefinition : 'function' Identifier parameterList '{' statement* '}' ; @@ -129,7 +138,7 @@ consoleParameterList ; functionCall - : Identifier expressionList // Only built-in functions are accepted + : Identifier expressionList // Built-in and user-defined functions are accepted ; expressionList diff --git a/packages/cashc/src/grammar/CashScript.interp b/packages/cashc/src/grammar/CashScript.interp index 0d3d6c29..8fc7f83f 100644 --- a/packages/cashc/src/grammar/CashScript.interp +++ b/packages/cashc/src/grammar/CashScript.interp @@ -13,6 +13,7 @@ null 'contract' '{' '}' +'library' 'function' '(' ',' @@ -137,6 +138,7 @@ null null null null +null VersionLiteral BooleanLiteral NumberUnit @@ -160,12 +162,14 @@ LINE_COMMENT rule names: sourceFile +topLevelDefinition pragmaDirective pragmaName pragmaValue versionConstraint versionOperator contractDefinition +libraryDefinition functionDefinition parameterList parameter @@ -199,4 +203,4 @@ typeCast atn: -[4, 1, 77, 426, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 1, 0, 5, 0, 76, 8, 0, 10, 0, 12, 0, 79, 9, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 3, 1, 3, 3, 3, 93, 8, 3, 1, 4, 3, 4, 96, 8, 4, 1, 4, 1, 4, 1, 5, 1, 5, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 5, 6, 107, 8, 6, 10, 6, 12, 6, 110, 9, 6, 1, 6, 1, 6, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 5, 7, 119, 8, 7, 10, 7, 12, 7, 122, 9, 7, 1, 7, 1, 7, 1, 8, 1, 8, 1, 8, 1, 8, 5, 8, 130, 8, 8, 10, 8, 12, 8, 133, 9, 8, 1, 8, 3, 8, 136, 8, 8, 3, 8, 138, 8, 8, 1, 8, 1, 8, 1, 9, 1, 9, 1, 9, 1, 10, 1, 10, 5, 10, 147, 8, 10, 10, 10, 12, 10, 150, 9, 10, 1, 10, 1, 10, 3, 10, 154, 8, 10, 1, 11, 1, 11, 1, 11, 1, 11, 3, 11, 160, 8, 11, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 3, 12, 168, 8, 12, 1, 13, 1, 13, 3, 13, 172, 8, 13, 1, 14, 1, 14, 5, 14, 176, 8, 14, 10, 14, 12, 14, 179, 9, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 16, 1, 16, 1, 16, 1, 16, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 3, 17, 204, 8, 17, 1, 17, 1, 17, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 3, 18, 213, 8, 18, 1, 18, 1, 18, 1, 19, 1, 19, 1, 19, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 3, 20, 227, 8, 20, 1, 21, 1, 21, 1, 21, 3, 21, 232, 8, 21, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 25, 1, 25, 3, 25, 260, 8, 25, 1, 26, 1, 26, 1, 27, 1, 27, 3, 27, 266, 8, 27, 1, 28, 1, 28, 1, 28, 1, 28, 5, 28, 272, 8, 28, 10, 28, 12, 28, 275, 9, 28, 1, 28, 3, 28, 278, 8, 28, 3, 28, 280, 8, 28, 1, 28, 1, 28, 1, 29, 1, 29, 1, 29, 1, 30, 1, 30, 1, 30, 1, 30, 5, 30, 291, 8, 30, 10, 30, 12, 30, 294, 9, 30, 1, 30, 3, 30, 297, 8, 30, 3, 30, 299, 8, 30, 1, 30, 1, 30, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 3, 31, 312, 8, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 5, 31, 338, 8, 31, 10, 31, 12, 31, 341, 9, 31, 1, 31, 3, 31, 344, 8, 31, 3, 31, 346, 8, 31, 1, 31, 1, 31, 1, 31, 1, 31, 3, 31, 352, 8, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 5, 31, 404, 8, 31, 10, 31, 12, 31, 407, 9, 31, 1, 32, 1, 32, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 3, 33, 416, 8, 33, 1, 34, 1, 34, 3, 34, 420, 8, 34, 1, 35, 1, 35, 1, 36, 1, 36, 1, 36, 0, 1, 62, 37, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 0, 12, 1, 0, 4, 10, 1, 0, 29, 33, 2, 0, 29, 33, 35, 38, 2, 0, 5, 5, 43, 44, 1, 0, 45, 47, 2, 0, 44, 44, 48, 48, 1, 0, 49, 50, 1, 0, 6, 9, 1, 0, 51, 52, 1, 0, 39, 40, 1, 0, 64, 66, 2, 0, 64, 65, 72, 72, 452, 0, 77, 1, 0, 0, 0, 2, 83, 1, 0, 0, 0, 4, 88, 1, 0, 0, 0, 6, 90, 1, 0, 0, 0, 8, 95, 1, 0, 0, 0, 10, 99, 1, 0, 0, 0, 12, 101, 1, 0, 0, 0, 14, 113, 1, 0, 0, 0, 16, 125, 1, 0, 0, 0, 18, 141, 1, 0, 0, 0, 20, 153, 1, 0, 0, 0, 22, 159, 1, 0, 0, 0, 24, 167, 1, 0, 0, 0, 26, 171, 1, 0, 0, 0, 28, 173, 1, 0, 0, 0, 30, 184, 1, 0, 0, 0, 32, 192, 1, 0, 0, 0, 34, 196, 1, 0, 0, 0, 36, 207, 1, 0, 0, 0, 38, 216, 1, 0, 0, 0, 40, 219, 1, 0, 0, 0, 42, 231, 1, 0, 0, 0, 44, 233, 1, 0, 0, 0, 46, 241, 1, 0, 0, 0, 48, 247, 1, 0, 0, 0, 50, 259, 1, 0, 0, 0, 52, 261, 1, 0, 0, 0, 54, 265, 1, 0, 0, 0, 56, 267, 1, 0, 0, 0, 58, 283, 1, 0, 0, 0, 60, 286, 1, 0, 0, 0, 62, 351, 1, 0, 0, 0, 64, 408, 1, 0, 0, 0, 66, 415, 1, 0, 0, 0, 68, 417, 1, 0, 0, 0, 70, 421, 1, 0, 0, 0, 72, 423, 1, 0, 0, 0, 74, 76, 3, 2, 1, 0, 75, 74, 1, 0, 0, 0, 76, 79, 1, 0, 0, 0, 77, 75, 1, 0, 0, 0, 77, 78, 1, 0, 0, 0, 78, 80, 1, 0, 0, 0, 79, 77, 1, 0, 0, 0, 80, 81, 3, 12, 6, 0, 81, 82, 5, 0, 0, 1, 82, 1, 1, 0, 0, 0, 83, 84, 5, 1, 0, 0, 84, 85, 3, 4, 2, 0, 85, 86, 3, 6, 3, 0, 86, 87, 5, 2, 0, 0, 87, 3, 1, 0, 0, 0, 88, 89, 5, 3, 0, 0, 89, 5, 1, 0, 0, 0, 90, 92, 3, 8, 4, 0, 91, 93, 3, 8, 4, 0, 92, 91, 1, 0, 0, 0, 92, 93, 1, 0, 0, 0, 93, 7, 1, 0, 0, 0, 94, 96, 3, 10, 5, 0, 95, 94, 1, 0, 0, 0, 95, 96, 1, 0, 0, 0, 96, 97, 1, 0, 0, 0, 97, 98, 5, 58, 0, 0, 98, 9, 1, 0, 0, 0, 99, 100, 7, 0, 0, 0, 100, 11, 1, 0, 0, 0, 101, 102, 5, 11, 0, 0, 102, 103, 5, 74, 0, 0, 103, 104, 3, 16, 8, 0, 104, 108, 5, 12, 0, 0, 105, 107, 3, 14, 7, 0, 106, 105, 1, 0, 0, 0, 107, 110, 1, 0, 0, 0, 108, 106, 1, 0, 0, 0, 108, 109, 1, 0, 0, 0, 109, 111, 1, 0, 0, 0, 110, 108, 1, 0, 0, 0, 111, 112, 5, 13, 0, 0, 112, 13, 1, 0, 0, 0, 113, 114, 5, 14, 0, 0, 114, 115, 5, 74, 0, 0, 115, 116, 3, 16, 8, 0, 116, 120, 5, 12, 0, 0, 117, 119, 3, 22, 11, 0, 118, 117, 1, 0, 0, 0, 119, 122, 1, 0, 0, 0, 120, 118, 1, 0, 0, 0, 120, 121, 1, 0, 0, 0, 121, 123, 1, 0, 0, 0, 122, 120, 1, 0, 0, 0, 123, 124, 5, 13, 0, 0, 124, 15, 1, 0, 0, 0, 125, 137, 5, 15, 0, 0, 126, 131, 3, 18, 9, 0, 127, 128, 5, 16, 0, 0, 128, 130, 3, 18, 9, 0, 129, 127, 1, 0, 0, 0, 130, 133, 1, 0, 0, 0, 131, 129, 1, 0, 0, 0, 131, 132, 1, 0, 0, 0, 132, 135, 1, 0, 0, 0, 133, 131, 1, 0, 0, 0, 134, 136, 5, 16, 0, 0, 135, 134, 1, 0, 0, 0, 135, 136, 1, 0, 0, 0, 136, 138, 1, 0, 0, 0, 137, 126, 1, 0, 0, 0, 137, 138, 1, 0, 0, 0, 138, 139, 1, 0, 0, 0, 139, 140, 5, 17, 0, 0, 140, 17, 1, 0, 0, 0, 141, 142, 3, 70, 35, 0, 142, 143, 5, 74, 0, 0, 143, 19, 1, 0, 0, 0, 144, 148, 5, 12, 0, 0, 145, 147, 3, 22, 11, 0, 146, 145, 1, 0, 0, 0, 147, 150, 1, 0, 0, 0, 148, 146, 1, 0, 0, 0, 148, 149, 1, 0, 0, 0, 149, 151, 1, 0, 0, 0, 150, 148, 1, 0, 0, 0, 151, 154, 5, 13, 0, 0, 152, 154, 3, 22, 11, 0, 153, 144, 1, 0, 0, 0, 153, 152, 1, 0, 0, 0, 154, 21, 1, 0, 0, 0, 155, 160, 3, 26, 13, 0, 156, 157, 3, 24, 12, 0, 157, 158, 5, 2, 0, 0, 158, 160, 1, 0, 0, 0, 159, 155, 1, 0, 0, 0, 159, 156, 1, 0, 0, 0, 160, 23, 1, 0, 0, 0, 161, 168, 3, 28, 14, 0, 162, 168, 3, 30, 15, 0, 163, 168, 3, 32, 16, 0, 164, 168, 3, 34, 17, 0, 165, 168, 3, 36, 18, 0, 166, 168, 3, 38, 19, 0, 167, 161, 1, 0, 0, 0, 167, 162, 1, 0, 0, 0, 167, 163, 1, 0, 0, 0, 167, 164, 1, 0, 0, 0, 167, 165, 1, 0, 0, 0, 167, 166, 1, 0, 0, 0, 168, 25, 1, 0, 0, 0, 169, 172, 3, 40, 20, 0, 170, 172, 3, 42, 21, 0, 171, 169, 1, 0, 0, 0, 171, 170, 1, 0, 0, 0, 172, 27, 1, 0, 0, 0, 173, 177, 3, 70, 35, 0, 174, 176, 3, 64, 32, 0, 175, 174, 1, 0, 0, 0, 176, 179, 1, 0, 0, 0, 177, 175, 1, 0, 0, 0, 177, 178, 1, 0, 0, 0, 178, 180, 1, 0, 0, 0, 179, 177, 1, 0, 0, 0, 180, 181, 5, 74, 0, 0, 181, 182, 5, 10, 0, 0, 182, 183, 3, 62, 31, 0, 183, 29, 1, 0, 0, 0, 184, 185, 3, 70, 35, 0, 185, 186, 5, 74, 0, 0, 186, 187, 5, 16, 0, 0, 187, 188, 3, 70, 35, 0, 188, 189, 5, 74, 0, 0, 189, 190, 5, 10, 0, 0, 190, 191, 3, 62, 31, 0, 191, 31, 1, 0, 0, 0, 192, 193, 5, 74, 0, 0, 193, 194, 5, 10, 0, 0, 194, 195, 3, 62, 31, 0, 195, 33, 1, 0, 0, 0, 196, 197, 5, 18, 0, 0, 197, 198, 5, 15, 0, 0, 198, 199, 5, 71, 0, 0, 199, 200, 5, 6, 0, 0, 200, 203, 3, 62, 31, 0, 201, 202, 5, 16, 0, 0, 202, 204, 3, 52, 26, 0, 203, 201, 1, 0, 0, 0, 203, 204, 1, 0, 0, 0, 204, 205, 1, 0, 0, 0, 205, 206, 5, 17, 0, 0, 206, 35, 1, 0, 0, 0, 207, 208, 5, 18, 0, 0, 208, 209, 5, 15, 0, 0, 209, 212, 3, 62, 31, 0, 210, 211, 5, 16, 0, 0, 211, 213, 3, 52, 26, 0, 212, 210, 1, 0, 0, 0, 212, 213, 1, 0, 0, 0, 213, 214, 1, 0, 0, 0, 214, 215, 5, 17, 0, 0, 215, 37, 1, 0, 0, 0, 216, 217, 5, 19, 0, 0, 217, 218, 3, 56, 28, 0, 218, 39, 1, 0, 0, 0, 219, 220, 5, 20, 0, 0, 220, 221, 5, 15, 0, 0, 221, 222, 3, 62, 31, 0, 222, 223, 5, 17, 0, 0, 223, 226, 3, 20, 10, 0, 224, 225, 5, 21, 0, 0, 225, 227, 3, 20, 10, 0, 226, 224, 1, 0, 0, 0, 226, 227, 1, 0, 0, 0, 227, 41, 1, 0, 0, 0, 228, 232, 3, 44, 22, 0, 229, 232, 3, 46, 23, 0, 230, 232, 3, 48, 24, 0, 231, 228, 1, 0, 0, 0, 231, 229, 1, 0, 0, 0, 231, 230, 1, 0, 0, 0, 232, 43, 1, 0, 0, 0, 233, 234, 5, 22, 0, 0, 234, 235, 3, 20, 10, 0, 235, 236, 5, 23, 0, 0, 236, 237, 5, 15, 0, 0, 237, 238, 3, 62, 31, 0, 238, 239, 5, 17, 0, 0, 239, 240, 5, 2, 0, 0, 240, 45, 1, 0, 0, 0, 241, 242, 5, 23, 0, 0, 242, 243, 5, 15, 0, 0, 243, 244, 3, 62, 31, 0, 244, 245, 5, 17, 0, 0, 245, 246, 3, 20, 10, 0, 246, 47, 1, 0, 0, 0, 247, 248, 5, 24, 0, 0, 248, 249, 5, 15, 0, 0, 249, 250, 3, 50, 25, 0, 250, 251, 5, 2, 0, 0, 251, 252, 3, 62, 31, 0, 252, 253, 5, 2, 0, 0, 253, 254, 3, 32, 16, 0, 254, 255, 5, 17, 0, 0, 255, 256, 3, 20, 10, 0, 256, 49, 1, 0, 0, 0, 257, 260, 3, 28, 14, 0, 258, 260, 3, 32, 16, 0, 259, 257, 1, 0, 0, 0, 259, 258, 1, 0, 0, 0, 260, 51, 1, 0, 0, 0, 261, 262, 5, 68, 0, 0, 262, 53, 1, 0, 0, 0, 263, 266, 5, 74, 0, 0, 264, 266, 3, 66, 33, 0, 265, 263, 1, 0, 0, 0, 265, 264, 1, 0, 0, 0, 266, 55, 1, 0, 0, 0, 267, 279, 5, 15, 0, 0, 268, 273, 3, 54, 27, 0, 269, 270, 5, 16, 0, 0, 270, 272, 3, 54, 27, 0, 271, 269, 1, 0, 0, 0, 272, 275, 1, 0, 0, 0, 273, 271, 1, 0, 0, 0, 273, 274, 1, 0, 0, 0, 274, 277, 1, 0, 0, 0, 275, 273, 1, 0, 0, 0, 276, 278, 5, 16, 0, 0, 277, 276, 1, 0, 0, 0, 277, 278, 1, 0, 0, 0, 278, 280, 1, 0, 0, 0, 279, 268, 1, 0, 0, 0, 279, 280, 1, 0, 0, 0, 280, 281, 1, 0, 0, 0, 281, 282, 5, 17, 0, 0, 282, 57, 1, 0, 0, 0, 283, 284, 5, 74, 0, 0, 284, 285, 3, 60, 30, 0, 285, 59, 1, 0, 0, 0, 286, 298, 5, 15, 0, 0, 287, 292, 3, 62, 31, 0, 288, 289, 5, 16, 0, 0, 289, 291, 3, 62, 31, 0, 290, 288, 1, 0, 0, 0, 291, 294, 1, 0, 0, 0, 292, 290, 1, 0, 0, 0, 292, 293, 1, 0, 0, 0, 293, 296, 1, 0, 0, 0, 294, 292, 1, 0, 0, 0, 295, 297, 5, 16, 0, 0, 296, 295, 1, 0, 0, 0, 296, 297, 1, 0, 0, 0, 297, 299, 1, 0, 0, 0, 298, 287, 1, 0, 0, 0, 298, 299, 1, 0, 0, 0, 299, 300, 1, 0, 0, 0, 300, 301, 5, 17, 0, 0, 301, 61, 1, 0, 0, 0, 302, 303, 6, 31, -1, 0, 303, 304, 5, 15, 0, 0, 304, 305, 3, 62, 31, 0, 305, 306, 5, 17, 0, 0, 306, 352, 1, 0, 0, 0, 307, 308, 3, 72, 36, 0, 308, 309, 5, 15, 0, 0, 309, 311, 3, 62, 31, 0, 310, 312, 5, 16, 0, 0, 311, 310, 1, 0, 0, 0, 311, 312, 1, 0, 0, 0, 312, 313, 1, 0, 0, 0, 313, 314, 5, 17, 0, 0, 314, 352, 1, 0, 0, 0, 315, 352, 3, 58, 29, 0, 316, 317, 5, 25, 0, 0, 317, 318, 5, 74, 0, 0, 318, 352, 3, 60, 30, 0, 319, 320, 5, 28, 0, 0, 320, 321, 5, 26, 0, 0, 321, 322, 3, 62, 31, 0, 322, 323, 5, 27, 0, 0, 323, 324, 7, 1, 0, 0, 324, 352, 1, 0, 0, 0, 325, 326, 5, 34, 0, 0, 326, 327, 5, 26, 0, 0, 327, 328, 3, 62, 31, 0, 328, 329, 5, 27, 0, 0, 329, 330, 7, 2, 0, 0, 330, 352, 1, 0, 0, 0, 331, 332, 7, 3, 0, 0, 332, 352, 3, 62, 31, 15, 333, 345, 5, 26, 0, 0, 334, 339, 3, 62, 31, 0, 335, 336, 5, 16, 0, 0, 336, 338, 3, 62, 31, 0, 337, 335, 1, 0, 0, 0, 338, 341, 1, 0, 0, 0, 339, 337, 1, 0, 0, 0, 339, 340, 1, 0, 0, 0, 340, 343, 1, 0, 0, 0, 341, 339, 1, 0, 0, 0, 342, 344, 5, 16, 0, 0, 343, 342, 1, 0, 0, 0, 343, 344, 1, 0, 0, 0, 344, 346, 1, 0, 0, 0, 345, 334, 1, 0, 0, 0, 345, 346, 1, 0, 0, 0, 346, 347, 1, 0, 0, 0, 347, 352, 5, 27, 0, 0, 348, 352, 5, 73, 0, 0, 349, 352, 5, 74, 0, 0, 350, 352, 3, 66, 33, 0, 351, 302, 1, 0, 0, 0, 351, 307, 1, 0, 0, 0, 351, 315, 1, 0, 0, 0, 351, 316, 1, 0, 0, 0, 351, 319, 1, 0, 0, 0, 351, 325, 1, 0, 0, 0, 351, 331, 1, 0, 0, 0, 351, 333, 1, 0, 0, 0, 351, 348, 1, 0, 0, 0, 351, 349, 1, 0, 0, 0, 351, 350, 1, 0, 0, 0, 352, 405, 1, 0, 0, 0, 353, 354, 10, 14, 0, 0, 354, 355, 7, 4, 0, 0, 355, 404, 3, 62, 31, 15, 356, 357, 10, 13, 0, 0, 357, 358, 7, 5, 0, 0, 358, 404, 3, 62, 31, 14, 359, 360, 10, 12, 0, 0, 360, 361, 7, 6, 0, 0, 361, 404, 3, 62, 31, 13, 362, 363, 10, 11, 0, 0, 363, 364, 7, 7, 0, 0, 364, 404, 3, 62, 31, 12, 365, 366, 10, 10, 0, 0, 366, 367, 7, 8, 0, 0, 367, 404, 3, 62, 31, 11, 368, 369, 10, 9, 0, 0, 369, 370, 5, 53, 0, 0, 370, 404, 3, 62, 31, 10, 371, 372, 10, 8, 0, 0, 372, 373, 5, 4, 0, 0, 373, 404, 3, 62, 31, 9, 374, 375, 10, 7, 0, 0, 375, 376, 5, 54, 0, 0, 376, 404, 3, 62, 31, 8, 377, 378, 10, 6, 0, 0, 378, 379, 5, 55, 0, 0, 379, 404, 3, 62, 31, 7, 380, 381, 10, 5, 0, 0, 381, 382, 5, 56, 0, 0, 382, 404, 3, 62, 31, 6, 383, 384, 10, 21, 0, 0, 384, 385, 5, 26, 0, 0, 385, 386, 5, 61, 0, 0, 386, 404, 5, 27, 0, 0, 387, 388, 10, 18, 0, 0, 388, 404, 7, 9, 0, 0, 389, 390, 10, 17, 0, 0, 390, 391, 5, 41, 0, 0, 391, 392, 5, 15, 0, 0, 392, 393, 3, 62, 31, 0, 393, 394, 5, 17, 0, 0, 394, 404, 1, 0, 0, 0, 395, 396, 10, 16, 0, 0, 396, 397, 5, 42, 0, 0, 397, 398, 5, 15, 0, 0, 398, 399, 3, 62, 31, 0, 399, 400, 5, 16, 0, 0, 400, 401, 3, 62, 31, 0, 401, 402, 5, 17, 0, 0, 402, 404, 1, 0, 0, 0, 403, 353, 1, 0, 0, 0, 403, 356, 1, 0, 0, 0, 403, 359, 1, 0, 0, 0, 403, 362, 1, 0, 0, 0, 403, 365, 1, 0, 0, 0, 403, 368, 1, 0, 0, 0, 403, 371, 1, 0, 0, 0, 403, 374, 1, 0, 0, 0, 403, 377, 1, 0, 0, 0, 403, 380, 1, 0, 0, 0, 403, 383, 1, 0, 0, 0, 403, 387, 1, 0, 0, 0, 403, 389, 1, 0, 0, 0, 403, 395, 1, 0, 0, 0, 404, 407, 1, 0, 0, 0, 405, 403, 1, 0, 0, 0, 405, 406, 1, 0, 0, 0, 406, 63, 1, 0, 0, 0, 407, 405, 1, 0, 0, 0, 408, 409, 5, 57, 0, 0, 409, 65, 1, 0, 0, 0, 410, 416, 5, 59, 0, 0, 411, 416, 3, 68, 34, 0, 412, 416, 5, 68, 0, 0, 413, 416, 5, 69, 0, 0, 414, 416, 5, 70, 0, 0, 415, 410, 1, 0, 0, 0, 415, 411, 1, 0, 0, 0, 415, 412, 1, 0, 0, 0, 415, 413, 1, 0, 0, 0, 415, 414, 1, 0, 0, 0, 416, 67, 1, 0, 0, 0, 417, 419, 5, 61, 0, 0, 418, 420, 5, 60, 0, 0, 419, 418, 1, 0, 0, 0, 419, 420, 1, 0, 0, 0, 420, 69, 1, 0, 0, 0, 421, 422, 7, 10, 0, 0, 422, 71, 1, 0, 0, 0, 423, 424, 7, 11, 0, 0, 424, 73, 1, 0, 0, 0, 35, 77, 92, 95, 108, 120, 131, 135, 137, 148, 153, 159, 167, 171, 177, 203, 212, 226, 231, 259, 265, 273, 277, 279, 292, 296, 298, 311, 339, 343, 345, 351, 403, 405, 415, 419] \ No newline at end of file +[4, 1, 78, 445, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 1, 0, 5, 0, 80, 8, 0, 10, 0, 12, 0, 83, 9, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 3, 1, 90, 8, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 3, 1, 3, 1, 4, 1, 4, 3, 4, 101, 8, 4, 1, 5, 3, 5, 104, 8, 5, 1, 5, 1, 5, 1, 6, 1, 6, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 5, 7, 115, 8, 7, 10, 7, 12, 7, 118, 9, 7, 1, 7, 1, 7, 1, 8, 1, 8, 1, 8, 1, 8, 5, 8, 126, 8, 8, 10, 8, 12, 8, 129, 9, 8, 1, 8, 1, 8, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 5, 9, 138, 8, 9, 10, 9, 12, 9, 141, 9, 9, 1, 9, 1, 9, 1, 10, 1, 10, 1, 10, 1, 10, 5, 10, 149, 8, 10, 10, 10, 12, 10, 152, 9, 10, 1, 10, 3, 10, 155, 8, 10, 3, 10, 157, 8, 10, 1, 10, 1, 10, 1, 11, 1, 11, 1, 11, 1, 12, 1, 12, 5, 12, 166, 8, 12, 10, 12, 12, 12, 169, 9, 12, 1, 12, 1, 12, 3, 12, 173, 8, 12, 1, 13, 1, 13, 1, 13, 1, 13, 3, 13, 179, 8, 13, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 3, 14, 187, 8, 14, 1, 15, 1, 15, 3, 15, 191, 8, 15, 1, 16, 1, 16, 5, 16, 195, 8, 16, 10, 16, 12, 16, 198, 9, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 18, 1, 18, 1, 18, 1, 18, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 3, 19, 223, 8, 19, 1, 19, 1, 19, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 3, 20, 232, 8, 20, 1, 20, 1, 20, 1, 21, 1, 21, 1, 21, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 3, 22, 246, 8, 22, 1, 23, 1, 23, 1, 23, 3, 23, 251, 8, 23, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 27, 1, 27, 3, 27, 279, 8, 27, 1, 28, 1, 28, 1, 29, 1, 29, 3, 29, 285, 8, 29, 1, 30, 1, 30, 1, 30, 1, 30, 5, 30, 291, 8, 30, 10, 30, 12, 30, 294, 9, 30, 1, 30, 3, 30, 297, 8, 30, 3, 30, 299, 8, 30, 1, 30, 1, 30, 1, 31, 1, 31, 1, 31, 1, 32, 1, 32, 1, 32, 1, 32, 5, 32, 310, 8, 32, 10, 32, 12, 32, 313, 9, 32, 1, 32, 3, 32, 316, 8, 32, 3, 32, 318, 8, 32, 1, 32, 1, 32, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 3, 33, 331, 8, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 5, 33, 357, 8, 33, 10, 33, 12, 33, 360, 9, 33, 1, 33, 3, 33, 363, 8, 33, 3, 33, 365, 8, 33, 1, 33, 1, 33, 1, 33, 1, 33, 3, 33, 371, 8, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 5, 33, 423, 8, 33, 10, 33, 12, 33, 426, 9, 33, 1, 34, 1, 34, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 3, 35, 435, 8, 35, 1, 36, 1, 36, 3, 36, 439, 8, 36, 1, 37, 1, 37, 1, 38, 1, 38, 1, 38, 0, 1, 66, 39, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 0, 12, 1, 0, 4, 10, 1, 0, 30, 34, 2, 0, 30, 34, 36, 39, 2, 0, 5, 5, 44, 45, 1, 0, 46, 48, 2, 0, 45, 45, 49, 49, 1, 0, 50, 51, 1, 0, 6, 9, 1, 0, 52, 53, 1, 0, 40, 41, 1, 0, 65, 67, 2, 0, 65, 66, 73, 73, 471, 0, 81, 1, 0, 0, 0, 2, 89, 1, 0, 0, 0, 4, 91, 1, 0, 0, 0, 6, 96, 1, 0, 0, 0, 8, 98, 1, 0, 0, 0, 10, 103, 1, 0, 0, 0, 12, 107, 1, 0, 0, 0, 14, 109, 1, 0, 0, 0, 16, 121, 1, 0, 0, 0, 18, 132, 1, 0, 0, 0, 20, 144, 1, 0, 0, 0, 22, 160, 1, 0, 0, 0, 24, 172, 1, 0, 0, 0, 26, 178, 1, 0, 0, 0, 28, 186, 1, 0, 0, 0, 30, 190, 1, 0, 0, 0, 32, 192, 1, 0, 0, 0, 34, 203, 1, 0, 0, 0, 36, 211, 1, 0, 0, 0, 38, 215, 1, 0, 0, 0, 40, 226, 1, 0, 0, 0, 42, 235, 1, 0, 0, 0, 44, 238, 1, 0, 0, 0, 46, 250, 1, 0, 0, 0, 48, 252, 1, 0, 0, 0, 50, 260, 1, 0, 0, 0, 52, 266, 1, 0, 0, 0, 54, 278, 1, 0, 0, 0, 56, 280, 1, 0, 0, 0, 58, 284, 1, 0, 0, 0, 60, 286, 1, 0, 0, 0, 62, 302, 1, 0, 0, 0, 64, 305, 1, 0, 0, 0, 66, 370, 1, 0, 0, 0, 68, 427, 1, 0, 0, 0, 70, 434, 1, 0, 0, 0, 72, 436, 1, 0, 0, 0, 74, 440, 1, 0, 0, 0, 76, 442, 1, 0, 0, 0, 78, 80, 3, 4, 2, 0, 79, 78, 1, 0, 0, 0, 80, 83, 1, 0, 0, 0, 81, 79, 1, 0, 0, 0, 81, 82, 1, 0, 0, 0, 82, 84, 1, 0, 0, 0, 83, 81, 1, 0, 0, 0, 84, 85, 3, 2, 1, 0, 85, 86, 5, 0, 0, 1, 86, 1, 1, 0, 0, 0, 87, 90, 3, 14, 7, 0, 88, 90, 3, 16, 8, 0, 89, 87, 1, 0, 0, 0, 89, 88, 1, 0, 0, 0, 90, 3, 1, 0, 0, 0, 91, 92, 5, 1, 0, 0, 92, 93, 3, 6, 3, 0, 93, 94, 3, 8, 4, 0, 94, 95, 5, 2, 0, 0, 95, 5, 1, 0, 0, 0, 96, 97, 5, 3, 0, 0, 97, 7, 1, 0, 0, 0, 98, 100, 3, 10, 5, 0, 99, 101, 3, 10, 5, 0, 100, 99, 1, 0, 0, 0, 100, 101, 1, 0, 0, 0, 101, 9, 1, 0, 0, 0, 102, 104, 3, 12, 6, 0, 103, 102, 1, 0, 0, 0, 103, 104, 1, 0, 0, 0, 104, 105, 1, 0, 0, 0, 105, 106, 5, 59, 0, 0, 106, 11, 1, 0, 0, 0, 107, 108, 7, 0, 0, 0, 108, 13, 1, 0, 0, 0, 109, 110, 5, 11, 0, 0, 110, 111, 5, 75, 0, 0, 111, 112, 3, 20, 10, 0, 112, 116, 5, 12, 0, 0, 113, 115, 3, 18, 9, 0, 114, 113, 1, 0, 0, 0, 115, 118, 1, 0, 0, 0, 116, 114, 1, 0, 0, 0, 116, 117, 1, 0, 0, 0, 117, 119, 1, 0, 0, 0, 118, 116, 1, 0, 0, 0, 119, 120, 5, 13, 0, 0, 120, 15, 1, 0, 0, 0, 121, 122, 5, 14, 0, 0, 122, 123, 5, 75, 0, 0, 123, 127, 5, 12, 0, 0, 124, 126, 3, 18, 9, 0, 125, 124, 1, 0, 0, 0, 126, 129, 1, 0, 0, 0, 127, 125, 1, 0, 0, 0, 127, 128, 1, 0, 0, 0, 128, 130, 1, 0, 0, 0, 129, 127, 1, 0, 0, 0, 130, 131, 5, 13, 0, 0, 131, 17, 1, 0, 0, 0, 132, 133, 5, 15, 0, 0, 133, 134, 5, 75, 0, 0, 134, 135, 3, 20, 10, 0, 135, 139, 5, 12, 0, 0, 136, 138, 3, 26, 13, 0, 137, 136, 1, 0, 0, 0, 138, 141, 1, 0, 0, 0, 139, 137, 1, 0, 0, 0, 139, 140, 1, 0, 0, 0, 140, 142, 1, 0, 0, 0, 141, 139, 1, 0, 0, 0, 142, 143, 5, 13, 0, 0, 143, 19, 1, 0, 0, 0, 144, 156, 5, 16, 0, 0, 145, 150, 3, 22, 11, 0, 146, 147, 5, 17, 0, 0, 147, 149, 3, 22, 11, 0, 148, 146, 1, 0, 0, 0, 149, 152, 1, 0, 0, 0, 150, 148, 1, 0, 0, 0, 150, 151, 1, 0, 0, 0, 151, 154, 1, 0, 0, 0, 152, 150, 1, 0, 0, 0, 153, 155, 5, 17, 0, 0, 154, 153, 1, 0, 0, 0, 154, 155, 1, 0, 0, 0, 155, 157, 1, 0, 0, 0, 156, 145, 1, 0, 0, 0, 156, 157, 1, 0, 0, 0, 157, 158, 1, 0, 0, 0, 158, 159, 5, 18, 0, 0, 159, 21, 1, 0, 0, 0, 160, 161, 3, 74, 37, 0, 161, 162, 5, 75, 0, 0, 162, 23, 1, 0, 0, 0, 163, 167, 5, 12, 0, 0, 164, 166, 3, 26, 13, 0, 165, 164, 1, 0, 0, 0, 166, 169, 1, 0, 0, 0, 167, 165, 1, 0, 0, 0, 167, 168, 1, 0, 0, 0, 168, 170, 1, 0, 0, 0, 169, 167, 1, 0, 0, 0, 170, 173, 5, 13, 0, 0, 171, 173, 3, 26, 13, 0, 172, 163, 1, 0, 0, 0, 172, 171, 1, 0, 0, 0, 173, 25, 1, 0, 0, 0, 174, 179, 3, 30, 15, 0, 175, 176, 3, 28, 14, 0, 176, 177, 5, 2, 0, 0, 177, 179, 1, 0, 0, 0, 178, 174, 1, 0, 0, 0, 178, 175, 1, 0, 0, 0, 179, 27, 1, 0, 0, 0, 180, 187, 3, 32, 16, 0, 181, 187, 3, 34, 17, 0, 182, 187, 3, 36, 18, 0, 183, 187, 3, 38, 19, 0, 184, 187, 3, 40, 20, 0, 185, 187, 3, 42, 21, 0, 186, 180, 1, 0, 0, 0, 186, 181, 1, 0, 0, 0, 186, 182, 1, 0, 0, 0, 186, 183, 1, 0, 0, 0, 186, 184, 1, 0, 0, 0, 186, 185, 1, 0, 0, 0, 187, 29, 1, 0, 0, 0, 188, 191, 3, 44, 22, 0, 189, 191, 3, 46, 23, 0, 190, 188, 1, 0, 0, 0, 190, 189, 1, 0, 0, 0, 191, 31, 1, 0, 0, 0, 192, 196, 3, 74, 37, 0, 193, 195, 3, 68, 34, 0, 194, 193, 1, 0, 0, 0, 195, 198, 1, 0, 0, 0, 196, 194, 1, 0, 0, 0, 196, 197, 1, 0, 0, 0, 197, 199, 1, 0, 0, 0, 198, 196, 1, 0, 0, 0, 199, 200, 5, 75, 0, 0, 200, 201, 5, 10, 0, 0, 201, 202, 3, 66, 33, 0, 202, 33, 1, 0, 0, 0, 203, 204, 3, 74, 37, 0, 204, 205, 5, 75, 0, 0, 205, 206, 5, 17, 0, 0, 206, 207, 3, 74, 37, 0, 207, 208, 5, 75, 0, 0, 208, 209, 5, 10, 0, 0, 209, 210, 3, 66, 33, 0, 210, 35, 1, 0, 0, 0, 211, 212, 5, 75, 0, 0, 212, 213, 5, 10, 0, 0, 213, 214, 3, 66, 33, 0, 214, 37, 1, 0, 0, 0, 215, 216, 5, 19, 0, 0, 216, 217, 5, 16, 0, 0, 217, 218, 5, 72, 0, 0, 218, 219, 5, 6, 0, 0, 219, 222, 3, 66, 33, 0, 220, 221, 5, 17, 0, 0, 221, 223, 3, 56, 28, 0, 222, 220, 1, 0, 0, 0, 222, 223, 1, 0, 0, 0, 223, 224, 1, 0, 0, 0, 224, 225, 5, 18, 0, 0, 225, 39, 1, 0, 0, 0, 226, 227, 5, 19, 0, 0, 227, 228, 5, 16, 0, 0, 228, 231, 3, 66, 33, 0, 229, 230, 5, 17, 0, 0, 230, 232, 3, 56, 28, 0, 231, 229, 1, 0, 0, 0, 231, 232, 1, 0, 0, 0, 232, 233, 1, 0, 0, 0, 233, 234, 5, 18, 0, 0, 234, 41, 1, 0, 0, 0, 235, 236, 5, 20, 0, 0, 236, 237, 3, 60, 30, 0, 237, 43, 1, 0, 0, 0, 238, 239, 5, 21, 0, 0, 239, 240, 5, 16, 0, 0, 240, 241, 3, 66, 33, 0, 241, 242, 5, 18, 0, 0, 242, 245, 3, 24, 12, 0, 243, 244, 5, 22, 0, 0, 244, 246, 3, 24, 12, 0, 245, 243, 1, 0, 0, 0, 245, 246, 1, 0, 0, 0, 246, 45, 1, 0, 0, 0, 247, 251, 3, 48, 24, 0, 248, 251, 3, 50, 25, 0, 249, 251, 3, 52, 26, 0, 250, 247, 1, 0, 0, 0, 250, 248, 1, 0, 0, 0, 250, 249, 1, 0, 0, 0, 251, 47, 1, 0, 0, 0, 252, 253, 5, 23, 0, 0, 253, 254, 3, 24, 12, 0, 254, 255, 5, 24, 0, 0, 255, 256, 5, 16, 0, 0, 256, 257, 3, 66, 33, 0, 257, 258, 5, 18, 0, 0, 258, 259, 5, 2, 0, 0, 259, 49, 1, 0, 0, 0, 260, 261, 5, 24, 0, 0, 261, 262, 5, 16, 0, 0, 262, 263, 3, 66, 33, 0, 263, 264, 5, 18, 0, 0, 264, 265, 3, 24, 12, 0, 265, 51, 1, 0, 0, 0, 266, 267, 5, 25, 0, 0, 267, 268, 5, 16, 0, 0, 268, 269, 3, 54, 27, 0, 269, 270, 5, 2, 0, 0, 270, 271, 3, 66, 33, 0, 271, 272, 5, 2, 0, 0, 272, 273, 3, 36, 18, 0, 273, 274, 5, 18, 0, 0, 274, 275, 3, 24, 12, 0, 275, 53, 1, 0, 0, 0, 276, 279, 3, 32, 16, 0, 277, 279, 3, 36, 18, 0, 278, 276, 1, 0, 0, 0, 278, 277, 1, 0, 0, 0, 279, 55, 1, 0, 0, 0, 280, 281, 5, 69, 0, 0, 281, 57, 1, 0, 0, 0, 282, 285, 5, 75, 0, 0, 283, 285, 3, 70, 35, 0, 284, 282, 1, 0, 0, 0, 284, 283, 1, 0, 0, 0, 285, 59, 1, 0, 0, 0, 286, 298, 5, 16, 0, 0, 287, 292, 3, 58, 29, 0, 288, 289, 5, 17, 0, 0, 289, 291, 3, 58, 29, 0, 290, 288, 1, 0, 0, 0, 291, 294, 1, 0, 0, 0, 292, 290, 1, 0, 0, 0, 292, 293, 1, 0, 0, 0, 293, 296, 1, 0, 0, 0, 294, 292, 1, 0, 0, 0, 295, 297, 5, 17, 0, 0, 296, 295, 1, 0, 0, 0, 296, 297, 1, 0, 0, 0, 297, 299, 1, 0, 0, 0, 298, 287, 1, 0, 0, 0, 298, 299, 1, 0, 0, 0, 299, 300, 1, 0, 0, 0, 300, 301, 5, 18, 0, 0, 301, 61, 1, 0, 0, 0, 302, 303, 5, 75, 0, 0, 303, 304, 3, 64, 32, 0, 304, 63, 1, 0, 0, 0, 305, 317, 5, 16, 0, 0, 306, 311, 3, 66, 33, 0, 307, 308, 5, 17, 0, 0, 308, 310, 3, 66, 33, 0, 309, 307, 1, 0, 0, 0, 310, 313, 1, 0, 0, 0, 311, 309, 1, 0, 0, 0, 311, 312, 1, 0, 0, 0, 312, 315, 1, 0, 0, 0, 313, 311, 1, 0, 0, 0, 314, 316, 5, 17, 0, 0, 315, 314, 1, 0, 0, 0, 315, 316, 1, 0, 0, 0, 316, 318, 1, 0, 0, 0, 317, 306, 1, 0, 0, 0, 317, 318, 1, 0, 0, 0, 318, 319, 1, 0, 0, 0, 319, 320, 5, 18, 0, 0, 320, 65, 1, 0, 0, 0, 321, 322, 6, 33, -1, 0, 322, 323, 5, 16, 0, 0, 323, 324, 3, 66, 33, 0, 324, 325, 5, 18, 0, 0, 325, 371, 1, 0, 0, 0, 326, 327, 3, 76, 38, 0, 327, 328, 5, 16, 0, 0, 328, 330, 3, 66, 33, 0, 329, 331, 5, 17, 0, 0, 330, 329, 1, 0, 0, 0, 330, 331, 1, 0, 0, 0, 331, 332, 1, 0, 0, 0, 332, 333, 5, 18, 0, 0, 333, 371, 1, 0, 0, 0, 334, 371, 3, 62, 31, 0, 335, 336, 5, 26, 0, 0, 336, 337, 5, 75, 0, 0, 337, 371, 3, 64, 32, 0, 338, 339, 5, 29, 0, 0, 339, 340, 5, 27, 0, 0, 340, 341, 3, 66, 33, 0, 341, 342, 5, 28, 0, 0, 342, 343, 7, 1, 0, 0, 343, 371, 1, 0, 0, 0, 344, 345, 5, 35, 0, 0, 345, 346, 5, 27, 0, 0, 346, 347, 3, 66, 33, 0, 347, 348, 5, 28, 0, 0, 348, 349, 7, 2, 0, 0, 349, 371, 1, 0, 0, 0, 350, 351, 7, 3, 0, 0, 351, 371, 3, 66, 33, 15, 352, 364, 5, 27, 0, 0, 353, 358, 3, 66, 33, 0, 354, 355, 5, 17, 0, 0, 355, 357, 3, 66, 33, 0, 356, 354, 1, 0, 0, 0, 357, 360, 1, 0, 0, 0, 358, 356, 1, 0, 0, 0, 358, 359, 1, 0, 0, 0, 359, 362, 1, 0, 0, 0, 360, 358, 1, 0, 0, 0, 361, 363, 5, 17, 0, 0, 362, 361, 1, 0, 0, 0, 362, 363, 1, 0, 0, 0, 363, 365, 1, 0, 0, 0, 364, 353, 1, 0, 0, 0, 364, 365, 1, 0, 0, 0, 365, 366, 1, 0, 0, 0, 366, 371, 5, 28, 0, 0, 367, 371, 5, 74, 0, 0, 368, 371, 5, 75, 0, 0, 369, 371, 3, 70, 35, 0, 370, 321, 1, 0, 0, 0, 370, 326, 1, 0, 0, 0, 370, 334, 1, 0, 0, 0, 370, 335, 1, 0, 0, 0, 370, 338, 1, 0, 0, 0, 370, 344, 1, 0, 0, 0, 370, 350, 1, 0, 0, 0, 370, 352, 1, 0, 0, 0, 370, 367, 1, 0, 0, 0, 370, 368, 1, 0, 0, 0, 370, 369, 1, 0, 0, 0, 371, 424, 1, 0, 0, 0, 372, 373, 10, 14, 0, 0, 373, 374, 7, 4, 0, 0, 374, 423, 3, 66, 33, 15, 375, 376, 10, 13, 0, 0, 376, 377, 7, 5, 0, 0, 377, 423, 3, 66, 33, 14, 378, 379, 10, 12, 0, 0, 379, 380, 7, 6, 0, 0, 380, 423, 3, 66, 33, 13, 381, 382, 10, 11, 0, 0, 382, 383, 7, 7, 0, 0, 383, 423, 3, 66, 33, 12, 384, 385, 10, 10, 0, 0, 385, 386, 7, 8, 0, 0, 386, 423, 3, 66, 33, 11, 387, 388, 10, 9, 0, 0, 388, 389, 5, 54, 0, 0, 389, 423, 3, 66, 33, 10, 390, 391, 10, 8, 0, 0, 391, 392, 5, 4, 0, 0, 392, 423, 3, 66, 33, 9, 393, 394, 10, 7, 0, 0, 394, 395, 5, 55, 0, 0, 395, 423, 3, 66, 33, 8, 396, 397, 10, 6, 0, 0, 397, 398, 5, 56, 0, 0, 398, 423, 3, 66, 33, 7, 399, 400, 10, 5, 0, 0, 400, 401, 5, 57, 0, 0, 401, 423, 3, 66, 33, 6, 402, 403, 10, 21, 0, 0, 403, 404, 5, 27, 0, 0, 404, 405, 5, 62, 0, 0, 405, 423, 5, 28, 0, 0, 406, 407, 10, 18, 0, 0, 407, 423, 7, 9, 0, 0, 408, 409, 10, 17, 0, 0, 409, 410, 5, 42, 0, 0, 410, 411, 5, 16, 0, 0, 411, 412, 3, 66, 33, 0, 412, 413, 5, 18, 0, 0, 413, 423, 1, 0, 0, 0, 414, 415, 10, 16, 0, 0, 415, 416, 5, 43, 0, 0, 416, 417, 5, 16, 0, 0, 417, 418, 3, 66, 33, 0, 418, 419, 5, 17, 0, 0, 419, 420, 3, 66, 33, 0, 420, 421, 5, 18, 0, 0, 421, 423, 1, 0, 0, 0, 422, 372, 1, 0, 0, 0, 422, 375, 1, 0, 0, 0, 422, 378, 1, 0, 0, 0, 422, 381, 1, 0, 0, 0, 422, 384, 1, 0, 0, 0, 422, 387, 1, 0, 0, 0, 422, 390, 1, 0, 0, 0, 422, 393, 1, 0, 0, 0, 422, 396, 1, 0, 0, 0, 422, 399, 1, 0, 0, 0, 422, 402, 1, 0, 0, 0, 422, 406, 1, 0, 0, 0, 422, 408, 1, 0, 0, 0, 422, 414, 1, 0, 0, 0, 423, 426, 1, 0, 0, 0, 424, 422, 1, 0, 0, 0, 424, 425, 1, 0, 0, 0, 425, 67, 1, 0, 0, 0, 426, 424, 1, 0, 0, 0, 427, 428, 5, 58, 0, 0, 428, 69, 1, 0, 0, 0, 429, 435, 5, 60, 0, 0, 430, 435, 3, 72, 36, 0, 431, 435, 5, 69, 0, 0, 432, 435, 5, 70, 0, 0, 433, 435, 5, 71, 0, 0, 434, 429, 1, 0, 0, 0, 434, 430, 1, 0, 0, 0, 434, 431, 1, 0, 0, 0, 434, 432, 1, 0, 0, 0, 434, 433, 1, 0, 0, 0, 435, 71, 1, 0, 0, 0, 436, 438, 5, 62, 0, 0, 437, 439, 5, 61, 0, 0, 438, 437, 1, 0, 0, 0, 438, 439, 1, 0, 0, 0, 439, 73, 1, 0, 0, 0, 440, 441, 7, 10, 0, 0, 441, 75, 1, 0, 0, 0, 442, 443, 7, 11, 0, 0, 443, 77, 1, 0, 0, 0, 37, 81, 89, 100, 103, 116, 127, 139, 150, 154, 156, 167, 172, 178, 186, 190, 196, 222, 231, 245, 250, 278, 284, 292, 296, 298, 311, 315, 317, 330, 358, 362, 364, 370, 422, 424, 434, 438] \ No newline at end of file diff --git a/packages/cashc/src/grammar/CashScript.tokens b/packages/cashc/src/grammar/CashScript.tokens index 3b21c6f2..216b113a 100644 --- a/packages/cashc/src/grammar/CashScript.tokens +++ b/packages/cashc/src/grammar/CashScript.tokens @@ -55,26 +55,27 @@ T__53=54 T__54=55 T__55=56 T__56=57 -VersionLiteral=58 -BooleanLiteral=59 -NumberUnit=60 -NumberLiteral=61 -NumberPart=62 -ExponentPart=63 -PrimitiveType=64 -UnboundedBytes=65 -BoundedBytes=66 -Bound=67 -StringLiteral=68 -DateLiteral=69 -HexLiteral=70 -TxVar=71 -UnsafeCast=72 -NullaryOp=73 -Identifier=74 -WHITESPACE=75 -COMMENT=76 -LINE_COMMENT=77 +T__57=58 +VersionLiteral=59 +BooleanLiteral=60 +NumberUnit=61 +NumberLiteral=62 +NumberPart=63 +ExponentPart=64 +PrimitiveType=65 +UnboundedBytes=66 +BoundedBytes=67 +Bound=68 +StringLiteral=69 +DateLiteral=70 +HexLiteral=71 +TxVar=72 +UnsafeCast=73 +NullaryOp=74 +Identifier=75 +WHITESPACE=76 +COMMENT=77 +LINE_COMMENT=78 'pragma'=1 ';'=2 'cashscript'=3 @@ -88,48 +89,49 @@ LINE_COMMENT=77 'contract'=11 '{'=12 '}'=13 -'function'=14 -'('=15 -','=16 -')'=17 -'require'=18 -'console.log'=19 -'if'=20 -'else'=21 -'do'=22 -'while'=23 -'for'=24 -'new'=25 -'['=26 -']'=27 -'tx.outputs'=28 -'.value'=29 -'.lockingBytecode'=30 -'.tokenCategory'=31 -'.nftCommitment'=32 -'.tokenAmount'=33 -'tx.inputs'=34 -'.outpointTransactionHash'=35 -'.outpointIndex'=36 -'.unlockingBytecode'=37 -'.sequenceNumber'=38 -'.reverse()'=39 -'.length'=40 -'.split'=41 -'.slice'=42 -'!'=43 -'-'=44 -'*'=45 -'/'=46 -'%'=47 -'+'=48 -'>>'=49 -'<<'=50 -'=='=51 -'!='=52 -'&'=53 -'|'=54 -'&&'=55 -'||'=56 -'constant'=57 -'bytes'=65 +'library'=14 +'function'=15 +'('=16 +','=17 +')'=18 +'require'=19 +'console.log'=20 +'if'=21 +'else'=22 +'do'=23 +'while'=24 +'for'=25 +'new'=26 +'['=27 +']'=28 +'tx.outputs'=29 +'.value'=30 +'.lockingBytecode'=31 +'.tokenCategory'=32 +'.nftCommitment'=33 +'.tokenAmount'=34 +'tx.inputs'=35 +'.outpointTransactionHash'=36 +'.outpointIndex'=37 +'.unlockingBytecode'=38 +'.sequenceNumber'=39 +'.reverse()'=40 +'.length'=41 +'.split'=42 +'.slice'=43 +'!'=44 +'-'=45 +'*'=46 +'/'=47 +'%'=48 +'+'=49 +'>>'=50 +'<<'=51 +'=='=52 +'!='=53 +'&'=54 +'|'=55 +'&&'=56 +'||'=57 +'constant'=58 +'bytes'=66 diff --git a/packages/cashc/src/grammar/CashScriptLexer.interp b/packages/cashc/src/grammar/CashScriptLexer.interp index b75527d4..90c886cc 100644 --- a/packages/cashc/src/grammar/CashScriptLexer.interp +++ b/packages/cashc/src/grammar/CashScriptLexer.interp @@ -13,6 +13,7 @@ null 'contract' '{' '}' +'library' 'function' '(' ',' @@ -137,6 +138,7 @@ null null null null +null VersionLiteral BooleanLiteral NumberUnit @@ -216,6 +218,7 @@ T__53 T__54 T__55 T__56 +T__57 VersionLiteral BooleanLiteral NumberUnit @@ -245,4 +248,4 @@ mode names: DEFAULT_MODE atn: -[4, 0, 77, 918, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7, 41, 2, 42, 7, 42, 2, 43, 7, 43, 2, 44, 7, 44, 2, 45, 7, 45, 2, 46, 7, 46, 2, 47, 7, 47, 2, 48, 7, 48, 2, 49, 7, 49, 2, 50, 7, 50, 2, 51, 7, 51, 2, 52, 7, 52, 2, 53, 7, 53, 2, 54, 7, 54, 2, 55, 7, 55, 2, 56, 7, 56, 2, 57, 7, 57, 2, 58, 7, 58, 2, 59, 7, 59, 2, 60, 7, 60, 2, 61, 7, 61, 2, 62, 7, 62, 2, 63, 7, 63, 2, 64, 7, 64, 2, 65, 7, 65, 2, 66, 7, 66, 2, 67, 7, 67, 2, 68, 7, 68, 2, 69, 7, 69, 2, 70, 7, 70, 2, 71, 7, 71, 2, 72, 7, 72, 2, 73, 7, 73, 2, 74, 7, 74, 2, 75, 7, 75, 2, 76, 7, 76, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 3, 1, 3, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 1, 6, 1, 6, 1, 7, 1, 7, 1, 8, 1, 8, 1, 8, 1, 9, 1, 9, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 11, 1, 11, 1, 12, 1, 12, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 14, 1, 14, 1, 15, 1, 15, 1, 16, 1, 16, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 19, 1, 19, 1, 19, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 21, 1, 21, 1, 21, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 23, 1, 23, 1, 23, 1, 23, 1, 24, 1, 24, 1, 24, 1, 24, 1, 25, 1, 25, 1, 26, 1, 26, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 42, 1, 42, 1, 43, 1, 43, 1, 44, 1, 44, 1, 45, 1, 45, 1, 46, 1, 46, 1, 47, 1, 47, 1, 48, 1, 48, 1, 48, 1, 49, 1, 49, 1, 49, 1, 50, 1, 50, 1, 50, 1, 51, 1, 51, 1, 51, 1, 52, 1, 52, 1, 53, 1, 53, 1, 54, 1, 54, 1, 54, 1, 55, 1, 55, 1, 55, 1, 56, 1, 56, 1, 56, 1, 56, 1, 56, 1, 56, 1, 56, 1, 56, 1, 56, 1, 57, 4, 57, 509, 8, 57, 11, 57, 12, 57, 510, 1, 57, 1, 57, 4, 57, 515, 8, 57, 11, 57, 12, 57, 516, 1, 57, 1, 57, 4, 57, 521, 8, 57, 11, 57, 12, 57, 522, 1, 58, 1, 58, 1, 58, 1, 58, 1, 58, 1, 58, 1, 58, 1, 58, 1, 58, 3, 58, 534, 8, 58, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 593, 8, 59, 1, 60, 3, 60, 596, 8, 60, 1, 60, 1, 60, 3, 60, 600, 8, 60, 1, 61, 4, 61, 603, 8, 61, 11, 61, 12, 61, 604, 1, 61, 1, 61, 4, 61, 609, 8, 61, 11, 61, 12, 61, 610, 5, 61, 613, 8, 61, 10, 61, 12, 61, 616, 9, 61, 1, 62, 1, 62, 1, 62, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 3, 63, 650, 8, 63, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 65, 1, 65, 1, 65, 1, 65, 1, 65, 1, 65, 1, 65, 1, 65, 1, 65, 1, 65, 1, 65, 3, 65, 669, 8, 65, 1, 66, 1, 66, 5, 66, 673, 8, 66, 10, 66, 12, 66, 676, 9, 66, 1, 67, 1, 67, 1, 67, 1, 67, 5, 67, 682, 8, 67, 10, 67, 12, 67, 685, 9, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 5, 67, 692, 8, 67, 10, 67, 12, 67, 695, 9, 67, 1, 67, 3, 67, 698, 8, 67, 1, 68, 1, 68, 1, 68, 1, 68, 1, 68, 1, 68, 1, 68, 1, 68, 1, 68, 1, 69, 1, 69, 1, 69, 5, 69, 712, 8, 69, 10, 69, 12, 69, 715, 9, 69, 1, 70, 1, 70, 1, 70, 1, 70, 1, 70, 1, 70, 1, 70, 1, 70, 1, 70, 1, 70, 1, 70, 1, 70, 1, 70, 1, 70, 1, 70, 3, 70, 732, 8, 70, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 3, 71, 769, 8, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 3, 71, 782, 8, 71, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 3, 72, 878, 8, 72, 1, 73, 1, 73, 5, 73, 882, 8, 73, 10, 73, 12, 73, 885, 9, 73, 1, 74, 4, 74, 888, 8, 74, 11, 74, 12, 74, 889, 1, 74, 1, 74, 1, 75, 1, 75, 1, 75, 1, 75, 5, 75, 898, 8, 75, 10, 75, 12, 75, 901, 9, 75, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, 1, 76, 1, 76, 1, 76, 1, 76, 5, 76, 912, 8, 76, 10, 76, 12, 76, 915, 9, 76, 1, 76, 1, 76, 3, 683, 693, 899, 0, 77, 1, 1, 3, 2, 5, 3, 7, 4, 9, 5, 11, 6, 13, 7, 15, 8, 17, 9, 19, 10, 21, 11, 23, 12, 25, 13, 27, 14, 29, 15, 31, 16, 33, 17, 35, 18, 37, 19, 39, 20, 41, 21, 43, 22, 45, 23, 47, 24, 49, 25, 51, 26, 53, 27, 55, 28, 57, 29, 59, 30, 61, 31, 63, 32, 65, 33, 67, 34, 69, 35, 71, 36, 73, 37, 75, 38, 77, 39, 79, 40, 81, 41, 83, 42, 85, 43, 87, 44, 89, 45, 91, 46, 93, 47, 95, 48, 97, 49, 99, 50, 101, 51, 103, 52, 105, 53, 107, 54, 109, 55, 111, 56, 113, 57, 115, 58, 117, 59, 119, 60, 121, 61, 123, 62, 125, 63, 127, 64, 129, 65, 131, 66, 133, 67, 135, 68, 137, 69, 139, 70, 141, 71, 143, 72, 145, 73, 147, 74, 149, 75, 151, 76, 153, 77, 1, 0, 11, 1, 0, 48, 57, 2, 0, 69, 69, 101, 101, 1, 0, 49, 57, 3, 0, 10, 10, 13, 13, 34, 34, 3, 0, 10, 10, 13, 13, 39, 39, 2, 0, 88, 88, 120, 120, 3, 0, 48, 57, 65, 70, 97, 102, 2, 0, 65, 90, 97, 122, 4, 0, 48, 57, 65, 90, 95, 95, 97, 122, 3, 0, 9, 10, 12, 13, 32, 32, 2, 0, 10, 10, 13, 13, 962, 0, 1, 1, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 5, 1, 0, 0, 0, 0, 7, 1, 0, 0, 0, 0, 9, 1, 0, 0, 0, 0, 11, 1, 0, 0, 0, 0, 13, 1, 0, 0, 0, 0, 15, 1, 0, 0, 0, 0, 17, 1, 0, 0, 0, 0, 19, 1, 0, 0, 0, 0, 21, 1, 0, 0, 0, 0, 23, 1, 0, 0, 0, 0, 25, 1, 0, 0, 0, 0, 27, 1, 0, 0, 0, 0, 29, 1, 0, 0, 0, 0, 31, 1, 0, 0, 0, 0, 33, 1, 0, 0, 0, 0, 35, 1, 0, 0, 0, 0, 37, 1, 0, 0, 0, 0, 39, 1, 0, 0, 0, 0, 41, 1, 0, 0, 0, 0, 43, 1, 0, 0, 0, 0, 45, 1, 0, 0, 0, 0, 47, 1, 0, 0, 0, 0, 49, 1, 0, 0, 0, 0, 51, 1, 0, 0, 0, 0, 53, 1, 0, 0, 0, 0, 55, 1, 0, 0, 0, 0, 57, 1, 0, 0, 0, 0, 59, 1, 0, 0, 0, 0, 61, 1, 0, 0, 0, 0, 63, 1, 0, 0, 0, 0, 65, 1, 0, 0, 0, 0, 67, 1, 0, 0, 0, 0, 69, 1, 0, 0, 0, 0, 71, 1, 0, 0, 0, 0, 73, 1, 0, 0, 0, 0, 75, 1, 0, 0, 0, 0, 77, 1, 0, 0, 0, 0, 79, 1, 0, 0, 0, 0, 81, 1, 0, 0, 0, 0, 83, 1, 0, 0, 0, 0, 85, 1, 0, 0, 0, 0, 87, 1, 0, 0, 0, 0, 89, 1, 0, 0, 0, 0, 91, 1, 0, 0, 0, 0, 93, 1, 0, 0, 0, 0, 95, 1, 0, 0, 0, 0, 97, 1, 0, 0, 0, 0, 99, 1, 0, 0, 0, 0, 101, 1, 0, 0, 0, 0, 103, 1, 0, 0, 0, 0, 105, 1, 0, 0, 0, 0, 107, 1, 0, 0, 0, 0, 109, 1, 0, 0, 0, 0, 111, 1, 0, 0, 0, 0, 113, 1, 0, 0, 0, 0, 115, 1, 0, 0, 0, 0, 117, 1, 0, 0, 0, 0, 119, 1, 0, 0, 0, 0, 121, 1, 0, 0, 0, 0, 123, 1, 0, 0, 0, 0, 125, 1, 0, 0, 0, 0, 127, 1, 0, 0, 0, 0, 129, 1, 0, 0, 0, 0, 131, 1, 0, 0, 0, 0, 133, 1, 0, 0, 0, 0, 135, 1, 0, 0, 0, 0, 137, 1, 0, 0, 0, 0, 139, 1, 0, 0, 0, 0, 141, 1, 0, 0, 0, 0, 143, 1, 0, 0, 0, 0, 145, 1, 0, 0, 0, 0, 147, 1, 0, 0, 0, 0, 149, 1, 0, 0, 0, 0, 151, 1, 0, 0, 0, 0, 153, 1, 0, 0, 0, 1, 155, 1, 0, 0, 0, 3, 162, 1, 0, 0, 0, 5, 164, 1, 0, 0, 0, 7, 175, 1, 0, 0, 0, 9, 177, 1, 0, 0, 0, 11, 179, 1, 0, 0, 0, 13, 182, 1, 0, 0, 0, 15, 184, 1, 0, 0, 0, 17, 186, 1, 0, 0, 0, 19, 189, 1, 0, 0, 0, 21, 191, 1, 0, 0, 0, 23, 200, 1, 0, 0, 0, 25, 202, 1, 0, 0, 0, 27, 204, 1, 0, 0, 0, 29, 213, 1, 0, 0, 0, 31, 215, 1, 0, 0, 0, 33, 217, 1, 0, 0, 0, 35, 219, 1, 0, 0, 0, 37, 227, 1, 0, 0, 0, 39, 239, 1, 0, 0, 0, 41, 242, 1, 0, 0, 0, 43, 247, 1, 0, 0, 0, 45, 250, 1, 0, 0, 0, 47, 256, 1, 0, 0, 0, 49, 260, 1, 0, 0, 0, 51, 264, 1, 0, 0, 0, 53, 266, 1, 0, 0, 0, 55, 268, 1, 0, 0, 0, 57, 279, 1, 0, 0, 0, 59, 286, 1, 0, 0, 0, 61, 303, 1, 0, 0, 0, 63, 318, 1, 0, 0, 0, 65, 333, 1, 0, 0, 0, 67, 346, 1, 0, 0, 0, 69, 356, 1, 0, 0, 0, 71, 381, 1, 0, 0, 0, 73, 396, 1, 0, 0, 0, 75, 415, 1, 0, 0, 0, 77, 431, 1, 0, 0, 0, 79, 442, 1, 0, 0, 0, 81, 450, 1, 0, 0, 0, 83, 457, 1, 0, 0, 0, 85, 464, 1, 0, 0, 0, 87, 466, 1, 0, 0, 0, 89, 468, 1, 0, 0, 0, 91, 470, 1, 0, 0, 0, 93, 472, 1, 0, 0, 0, 95, 474, 1, 0, 0, 0, 97, 476, 1, 0, 0, 0, 99, 479, 1, 0, 0, 0, 101, 482, 1, 0, 0, 0, 103, 485, 1, 0, 0, 0, 105, 488, 1, 0, 0, 0, 107, 490, 1, 0, 0, 0, 109, 492, 1, 0, 0, 0, 111, 495, 1, 0, 0, 0, 113, 498, 1, 0, 0, 0, 115, 508, 1, 0, 0, 0, 117, 533, 1, 0, 0, 0, 119, 592, 1, 0, 0, 0, 121, 595, 1, 0, 0, 0, 123, 602, 1, 0, 0, 0, 125, 617, 1, 0, 0, 0, 127, 649, 1, 0, 0, 0, 129, 651, 1, 0, 0, 0, 131, 668, 1, 0, 0, 0, 133, 670, 1, 0, 0, 0, 135, 697, 1, 0, 0, 0, 137, 699, 1, 0, 0, 0, 139, 708, 1, 0, 0, 0, 141, 731, 1, 0, 0, 0, 143, 781, 1, 0, 0, 0, 145, 877, 1, 0, 0, 0, 147, 879, 1, 0, 0, 0, 149, 887, 1, 0, 0, 0, 151, 893, 1, 0, 0, 0, 153, 907, 1, 0, 0, 0, 155, 156, 5, 112, 0, 0, 156, 157, 5, 114, 0, 0, 157, 158, 5, 97, 0, 0, 158, 159, 5, 103, 0, 0, 159, 160, 5, 109, 0, 0, 160, 161, 5, 97, 0, 0, 161, 2, 1, 0, 0, 0, 162, 163, 5, 59, 0, 0, 163, 4, 1, 0, 0, 0, 164, 165, 5, 99, 0, 0, 165, 166, 5, 97, 0, 0, 166, 167, 5, 115, 0, 0, 167, 168, 5, 104, 0, 0, 168, 169, 5, 115, 0, 0, 169, 170, 5, 99, 0, 0, 170, 171, 5, 114, 0, 0, 171, 172, 5, 105, 0, 0, 172, 173, 5, 112, 0, 0, 173, 174, 5, 116, 0, 0, 174, 6, 1, 0, 0, 0, 175, 176, 5, 94, 0, 0, 176, 8, 1, 0, 0, 0, 177, 178, 5, 126, 0, 0, 178, 10, 1, 0, 0, 0, 179, 180, 5, 62, 0, 0, 180, 181, 5, 61, 0, 0, 181, 12, 1, 0, 0, 0, 182, 183, 5, 62, 0, 0, 183, 14, 1, 0, 0, 0, 184, 185, 5, 60, 0, 0, 185, 16, 1, 0, 0, 0, 186, 187, 5, 60, 0, 0, 187, 188, 5, 61, 0, 0, 188, 18, 1, 0, 0, 0, 189, 190, 5, 61, 0, 0, 190, 20, 1, 0, 0, 0, 191, 192, 5, 99, 0, 0, 192, 193, 5, 111, 0, 0, 193, 194, 5, 110, 0, 0, 194, 195, 5, 116, 0, 0, 195, 196, 5, 114, 0, 0, 196, 197, 5, 97, 0, 0, 197, 198, 5, 99, 0, 0, 198, 199, 5, 116, 0, 0, 199, 22, 1, 0, 0, 0, 200, 201, 5, 123, 0, 0, 201, 24, 1, 0, 0, 0, 202, 203, 5, 125, 0, 0, 203, 26, 1, 0, 0, 0, 204, 205, 5, 102, 0, 0, 205, 206, 5, 117, 0, 0, 206, 207, 5, 110, 0, 0, 207, 208, 5, 99, 0, 0, 208, 209, 5, 116, 0, 0, 209, 210, 5, 105, 0, 0, 210, 211, 5, 111, 0, 0, 211, 212, 5, 110, 0, 0, 212, 28, 1, 0, 0, 0, 213, 214, 5, 40, 0, 0, 214, 30, 1, 0, 0, 0, 215, 216, 5, 44, 0, 0, 216, 32, 1, 0, 0, 0, 217, 218, 5, 41, 0, 0, 218, 34, 1, 0, 0, 0, 219, 220, 5, 114, 0, 0, 220, 221, 5, 101, 0, 0, 221, 222, 5, 113, 0, 0, 222, 223, 5, 117, 0, 0, 223, 224, 5, 105, 0, 0, 224, 225, 5, 114, 0, 0, 225, 226, 5, 101, 0, 0, 226, 36, 1, 0, 0, 0, 227, 228, 5, 99, 0, 0, 228, 229, 5, 111, 0, 0, 229, 230, 5, 110, 0, 0, 230, 231, 5, 115, 0, 0, 231, 232, 5, 111, 0, 0, 232, 233, 5, 108, 0, 0, 233, 234, 5, 101, 0, 0, 234, 235, 5, 46, 0, 0, 235, 236, 5, 108, 0, 0, 236, 237, 5, 111, 0, 0, 237, 238, 5, 103, 0, 0, 238, 38, 1, 0, 0, 0, 239, 240, 5, 105, 0, 0, 240, 241, 5, 102, 0, 0, 241, 40, 1, 0, 0, 0, 242, 243, 5, 101, 0, 0, 243, 244, 5, 108, 0, 0, 244, 245, 5, 115, 0, 0, 245, 246, 5, 101, 0, 0, 246, 42, 1, 0, 0, 0, 247, 248, 5, 100, 0, 0, 248, 249, 5, 111, 0, 0, 249, 44, 1, 0, 0, 0, 250, 251, 5, 119, 0, 0, 251, 252, 5, 104, 0, 0, 252, 253, 5, 105, 0, 0, 253, 254, 5, 108, 0, 0, 254, 255, 5, 101, 0, 0, 255, 46, 1, 0, 0, 0, 256, 257, 5, 102, 0, 0, 257, 258, 5, 111, 0, 0, 258, 259, 5, 114, 0, 0, 259, 48, 1, 0, 0, 0, 260, 261, 5, 110, 0, 0, 261, 262, 5, 101, 0, 0, 262, 263, 5, 119, 0, 0, 263, 50, 1, 0, 0, 0, 264, 265, 5, 91, 0, 0, 265, 52, 1, 0, 0, 0, 266, 267, 5, 93, 0, 0, 267, 54, 1, 0, 0, 0, 268, 269, 5, 116, 0, 0, 269, 270, 5, 120, 0, 0, 270, 271, 5, 46, 0, 0, 271, 272, 5, 111, 0, 0, 272, 273, 5, 117, 0, 0, 273, 274, 5, 116, 0, 0, 274, 275, 5, 112, 0, 0, 275, 276, 5, 117, 0, 0, 276, 277, 5, 116, 0, 0, 277, 278, 5, 115, 0, 0, 278, 56, 1, 0, 0, 0, 279, 280, 5, 46, 0, 0, 280, 281, 5, 118, 0, 0, 281, 282, 5, 97, 0, 0, 282, 283, 5, 108, 0, 0, 283, 284, 5, 117, 0, 0, 284, 285, 5, 101, 0, 0, 285, 58, 1, 0, 0, 0, 286, 287, 5, 46, 0, 0, 287, 288, 5, 108, 0, 0, 288, 289, 5, 111, 0, 0, 289, 290, 5, 99, 0, 0, 290, 291, 5, 107, 0, 0, 291, 292, 5, 105, 0, 0, 292, 293, 5, 110, 0, 0, 293, 294, 5, 103, 0, 0, 294, 295, 5, 66, 0, 0, 295, 296, 5, 121, 0, 0, 296, 297, 5, 116, 0, 0, 297, 298, 5, 101, 0, 0, 298, 299, 5, 99, 0, 0, 299, 300, 5, 111, 0, 0, 300, 301, 5, 100, 0, 0, 301, 302, 5, 101, 0, 0, 302, 60, 1, 0, 0, 0, 303, 304, 5, 46, 0, 0, 304, 305, 5, 116, 0, 0, 305, 306, 5, 111, 0, 0, 306, 307, 5, 107, 0, 0, 307, 308, 5, 101, 0, 0, 308, 309, 5, 110, 0, 0, 309, 310, 5, 67, 0, 0, 310, 311, 5, 97, 0, 0, 311, 312, 5, 116, 0, 0, 312, 313, 5, 101, 0, 0, 313, 314, 5, 103, 0, 0, 314, 315, 5, 111, 0, 0, 315, 316, 5, 114, 0, 0, 316, 317, 5, 121, 0, 0, 317, 62, 1, 0, 0, 0, 318, 319, 5, 46, 0, 0, 319, 320, 5, 110, 0, 0, 320, 321, 5, 102, 0, 0, 321, 322, 5, 116, 0, 0, 322, 323, 5, 67, 0, 0, 323, 324, 5, 111, 0, 0, 324, 325, 5, 109, 0, 0, 325, 326, 5, 109, 0, 0, 326, 327, 5, 105, 0, 0, 327, 328, 5, 116, 0, 0, 328, 329, 5, 109, 0, 0, 329, 330, 5, 101, 0, 0, 330, 331, 5, 110, 0, 0, 331, 332, 5, 116, 0, 0, 332, 64, 1, 0, 0, 0, 333, 334, 5, 46, 0, 0, 334, 335, 5, 116, 0, 0, 335, 336, 5, 111, 0, 0, 336, 337, 5, 107, 0, 0, 337, 338, 5, 101, 0, 0, 338, 339, 5, 110, 0, 0, 339, 340, 5, 65, 0, 0, 340, 341, 5, 109, 0, 0, 341, 342, 5, 111, 0, 0, 342, 343, 5, 117, 0, 0, 343, 344, 5, 110, 0, 0, 344, 345, 5, 116, 0, 0, 345, 66, 1, 0, 0, 0, 346, 347, 5, 116, 0, 0, 347, 348, 5, 120, 0, 0, 348, 349, 5, 46, 0, 0, 349, 350, 5, 105, 0, 0, 350, 351, 5, 110, 0, 0, 351, 352, 5, 112, 0, 0, 352, 353, 5, 117, 0, 0, 353, 354, 5, 116, 0, 0, 354, 355, 5, 115, 0, 0, 355, 68, 1, 0, 0, 0, 356, 357, 5, 46, 0, 0, 357, 358, 5, 111, 0, 0, 358, 359, 5, 117, 0, 0, 359, 360, 5, 116, 0, 0, 360, 361, 5, 112, 0, 0, 361, 362, 5, 111, 0, 0, 362, 363, 5, 105, 0, 0, 363, 364, 5, 110, 0, 0, 364, 365, 5, 116, 0, 0, 365, 366, 5, 84, 0, 0, 366, 367, 5, 114, 0, 0, 367, 368, 5, 97, 0, 0, 368, 369, 5, 110, 0, 0, 369, 370, 5, 115, 0, 0, 370, 371, 5, 97, 0, 0, 371, 372, 5, 99, 0, 0, 372, 373, 5, 116, 0, 0, 373, 374, 5, 105, 0, 0, 374, 375, 5, 111, 0, 0, 375, 376, 5, 110, 0, 0, 376, 377, 5, 72, 0, 0, 377, 378, 5, 97, 0, 0, 378, 379, 5, 115, 0, 0, 379, 380, 5, 104, 0, 0, 380, 70, 1, 0, 0, 0, 381, 382, 5, 46, 0, 0, 382, 383, 5, 111, 0, 0, 383, 384, 5, 117, 0, 0, 384, 385, 5, 116, 0, 0, 385, 386, 5, 112, 0, 0, 386, 387, 5, 111, 0, 0, 387, 388, 5, 105, 0, 0, 388, 389, 5, 110, 0, 0, 389, 390, 5, 116, 0, 0, 390, 391, 5, 73, 0, 0, 391, 392, 5, 110, 0, 0, 392, 393, 5, 100, 0, 0, 393, 394, 5, 101, 0, 0, 394, 395, 5, 120, 0, 0, 395, 72, 1, 0, 0, 0, 396, 397, 5, 46, 0, 0, 397, 398, 5, 117, 0, 0, 398, 399, 5, 110, 0, 0, 399, 400, 5, 108, 0, 0, 400, 401, 5, 111, 0, 0, 401, 402, 5, 99, 0, 0, 402, 403, 5, 107, 0, 0, 403, 404, 5, 105, 0, 0, 404, 405, 5, 110, 0, 0, 405, 406, 5, 103, 0, 0, 406, 407, 5, 66, 0, 0, 407, 408, 5, 121, 0, 0, 408, 409, 5, 116, 0, 0, 409, 410, 5, 101, 0, 0, 410, 411, 5, 99, 0, 0, 411, 412, 5, 111, 0, 0, 412, 413, 5, 100, 0, 0, 413, 414, 5, 101, 0, 0, 414, 74, 1, 0, 0, 0, 415, 416, 5, 46, 0, 0, 416, 417, 5, 115, 0, 0, 417, 418, 5, 101, 0, 0, 418, 419, 5, 113, 0, 0, 419, 420, 5, 117, 0, 0, 420, 421, 5, 101, 0, 0, 421, 422, 5, 110, 0, 0, 422, 423, 5, 99, 0, 0, 423, 424, 5, 101, 0, 0, 424, 425, 5, 78, 0, 0, 425, 426, 5, 117, 0, 0, 426, 427, 5, 109, 0, 0, 427, 428, 5, 98, 0, 0, 428, 429, 5, 101, 0, 0, 429, 430, 5, 114, 0, 0, 430, 76, 1, 0, 0, 0, 431, 432, 5, 46, 0, 0, 432, 433, 5, 114, 0, 0, 433, 434, 5, 101, 0, 0, 434, 435, 5, 118, 0, 0, 435, 436, 5, 101, 0, 0, 436, 437, 5, 114, 0, 0, 437, 438, 5, 115, 0, 0, 438, 439, 5, 101, 0, 0, 439, 440, 5, 40, 0, 0, 440, 441, 5, 41, 0, 0, 441, 78, 1, 0, 0, 0, 442, 443, 5, 46, 0, 0, 443, 444, 5, 108, 0, 0, 444, 445, 5, 101, 0, 0, 445, 446, 5, 110, 0, 0, 446, 447, 5, 103, 0, 0, 447, 448, 5, 116, 0, 0, 448, 449, 5, 104, 0, 0, 449, 80, 1, 0, 0, 0, 450, 451, 5, 46, 0, 0, 451, 452, 5, 115, 0, 0, 452, 453, 5, 112, 0, 0, 453, 454, 5, 108, 0, 0, 454, 455, 5, 105, 0, 0, 455, 456, 5, 116, 0, 0, 456, 82, 1, 0, 0, 0, 457, 458, 5, 46, 0, 0, 458, 459, 5, 115, 0, 0, 459, 460, 5, 108, 0, 0, 460, 461, 5, 105, 0, 0, 461, 462, 5, 99, 0, 0, 462, 463, 5, 101, 0, 0, 463, 84, 1, 0, 0, 0, 464, 465, 5, 33, 0, 0, 465, 86, 1, 0, 0, 0, 466, 467, 5, 45, 0, 0, 467, 88, 1, 0, 0, 0, 468, 469, 5, 42, 0, 0, 469, 90, 1, 0, 0, 0, 470, 471, 5, 47, 0, 0, 471, 92, 1, 0, 0, 0, 472, 473, 5, 37, 0, 0, 473, 94, 1, 0, 0, 0, 474, 475, 5, 43, 0, 0, 475, 96, 1, 0, 0, 0, 476, 477, 5, 62, 0, 0, 477, 478, 5, 62, 0, 0, 478, 98, 1, 0, 0, 0, 479, 480, 5, 60, 0, 0, 480, 481, 5, 60, 0, 0, 481, 100, 1, 0, 0, 0, 482, 483, 5, 61, 0, 0, 483, 484, 5, 61, 0, 0, 484, 102, 1, 0, 0, 0, 485, 486, 5, 33, 0, 0, 486, 487, 5, 61, 0, 0, 487, 104, 1, 0, 0, 0, 488, 489, 5, 38, 0, 0, 489, 106, 1, 0, 0, 0, 490, 491, 5, 124, 0, 0, 491, 108, 1, 0, 0, 0, 492, 493, 5, 38, 0, 0, 493, 494, 5, 38, 0, 0, 494, 110, 1, 0, 0, 0, 495, 496, 5, 124, 0, 0, 496, 497, 5, 124, 0, 0, 497, 112, 1, 0, 0, 0, 498, 499, 5, 99, 0, 0, 499, 500, 5, 111, 0, 0, 500, 501, 5, 110, 0, 0, 501, 502, 5, 115, 0, 0, 502, 503, 5, 116, 0, 0, 503, 504, 5, 97, 0, 0, 504, 505, 5, 110, 0, 0, 505, 506, 5, 116, 0, 0, 506, 114, 1, 0, 0, 0, 507, 509, 7, 0, 0, 0, 508, 507, 1, 0, 0, 0, 509, 510, 1, 0, 0, 0, 510, 508, 1, 0, 0, 0, 510, 511, 1, 0, 0, 0, 511, 512, 1, 0, 0, 0, 512, 514, 5, 46, 0, 0, 513, 515, 7, 0, 0, 0, 514, 513, 1, 0, 0, 0, 515, 516, 1, 0, 0, 0, 516, 514, 1, 0, 0, 0, 516, 517, 1, 0, 0, 0, 517, 518, 1, 0, 0, 0, 518, 520, 5, 46, 0, 0, 519, 521, 7, 0, 0, 0, 520, 519, 1, 0, 0, 0, 521, 522, 1, 0, 0, 0, 522, 520, 1, 0, 0, 0, 522, 523, 1, 0, 0, 0, 523, 116, 1, 0, 0, 0, 524, 525, 5, 116, 0, 0, 525, 526, 5, 114, 0, 0, 526, 527, 5, 117, 0, 0, 527, 534, 5, 101, 0, 0, 528, 529, 5, 102, 0, 0, 529, 530, 5, 97, 0, 0, 530, 531, 5, 108, 0, 0, 531, 532, 5, 115, 0, 0, 532, 534, 5, 101, 0, 0, 533, 524, 1, 0, 0, 0, 533, 528, 1, 0, 0, 0, 534, 118, 1, 0, 0, 0, 535, 536, 5, 115, 0, 0, 536, 537, 5, 97, 0, 0, 537, 538, 5, 116, 0, 0, 538, 539, 5, 111, 0, 0, 539, 540, 5, 115, 0, 0, 540, 541, 5, 104, 0, 0, 541, 542, 5, 105, 0, 0, 542, 593, 5, 115, 0, 0, 543, 544, 5, 115, 0, 0, 544, 545, 5, 97, 0, 0, 545, 546, 5, 116, 0, 0, 546, 593, 5, 115, 0, 0, 547, 548, 5, 102, 0, 0, 548, 549, 5, 105, 0, 0, 549, 550, 5, 110, 0, 0, 550, 551, 5, 110, 0, 0, 551, 552, 5, 101, 0, 0, 552, 593, 5, 121, 0, 0, 553, 554, 5, 98, 0, 0, 554, 555, 5, 105, 0, 0, 555, 556, 5, 116, 0, 0, 556, 593, 5, 115, 0, 0, 557, 558, 5, 98, 0, 0, 558, 559, 5, 105, 0, 0, 559, 560, 5, 116, 0, 0, 560, 561, 5, 99, 0, 0, 561, 562, 5, 111, 0, 0, 562, 563, 5, 105, 0, 0, 563, 593, 5, 110, 0, 0, 564, 565, 5, 115, 0, 0, 565, 566, 5, 101, 0, 0, 566, 567, 5, 99, 0, 0, 567, 568, 5, 111, 0, 0, 568, 569, 5, 110, 0, 0, 569, 570, 5, 100, 0, 0, 570, 593, 5, 115, 0, 0, 571, 572, 5, 109, 0, 0, 572, 573, 5, 105, 0, 0, 573, 574, 5, 110, 0, 0, 574, 575, 5, 117, 0, 0, 575, 576, 5, 116, 0, 0, 576, 577, 5, 101, 0, 0, 577, 593, 5, 115, 0, 0, 578, 579, 5, 104, 0, 0, 579, 580, 5, 111, 0, 0, 580, 581, 5, 117, 0, 0, 581, 582, 5, 114, 0, 0, 582, 593, 5, 115, 0, 0, 583, 584, 5, 100, 0, 0, 584, 585, 5, 97, 0, 0, 585, 586, 5, 121, 0, 0, 586, 593, 5, 115, 0, 0, 587, 588, 5, 119, 0, 0, 588, 589, 5, 101, 0, 0, 589, 590, 5, 101, 0, 0, 590, 591, 5, 107, 0, 0, 591, 593, 5, 115, 0, 0, 592, 535, 1, 0, 0, 0, 592, 543, 1, 0, 0, 0, 592, 547, 1, 0, 0, 0, 592, 553, 1, 0, 0, 0, 592, 557, 1, 0, 0, 0, 592, 564, 1, 0, 0, 0, 592, 571, 1, 0, 0, 0, 592, 578, 1, 0, 0, 0, 592, 583, 1, 0, 0, 0, 592, 587, 1, 0, 0, 0, 593, 120, 1, 0, 0, 0, 594, 596, 5, 45, 0, 0, 595, 594, 1, 0, 0, 0, 595, 596, 1, 0, 0, 0, 596, 597, 1, 0, 0, 0, 597, 599, 3, 123, 61, 0, 598, 600, 3, 125, 62, 0, 599, 598, 1, 0, 0, 0, 599, 600, 1, 0, 0, 0, 600, 122, 1, 0, 0, 0, 601, 603, 7, 0, 0, 0, 602, 601, 1, 0, 0, 0, 603, 604, 1, 0, 0, 0, 604, 602, 1, 0, 0, 0, 604, 605, 1, 0, 0, 0, 605, 614, 1, 0, 0, 0, 606, 608, 5, 95, 0, 0, 607, 609, 7, 0, 0, 0, 608, 607, 1, 0, 0, 0, 609, 610, 1, 0, 0, 0, 610, 608, 1, 0, 0, 0, 610, 611, 1, 0, 0, 0, 611, 613, 1, 0, 0, 0, 612, 606, 1, 0, 0, 0, 613, 616, 1, 0, 0, 0, 614, 612, 1, 0, 0, 0, 614, 615, 1, 0, 0, 0, 615, 124, 1, 0, 0, 0, 616, 614, 1, 0, 0, 0, 617, 618, 7, 1, 0, 0, 618, 619, 3, 123, 61, 0, 619, 126, 1, 0, 0, 0, 620, 621, 5, 105, 0, 0, 621, 622, 5, 110, 0, 0, 622, 650, 5, 116, 0, 0, 623, 624, 5, 98, 0, 0, 624, 625, 5, 111, 0, 0, 625, 626, 5, 111, 0, 0, 626, 650, 5, 108, 0, 0, 627, 628, 5, 115, 0, 0, 628, 629, 5, 116, 0, 0, 629, 630, 5, 114, 0, 0, 630, 631, 5, 105, 0, 0, 631, 632, 5, 110, 0, 0, 632, 650, 5, 103, 0, 0, 633, 634, 5, 112, 0, 0, 634, 635, 5, 117, 0, 0, 635, 636, 5, 98, 0, 0, 636, 637, 5, 107, 0, 0, 637, 638, 5, 101, 0, 0, 638, 650, 5, 121, 0, 0, 639, 640, 5, 115, 0, 0, 640, 641, 5, 105, 0, 0, 641, 650, 5, 103, 0, 0, 642, 643, 5, 100, 0, 0, 643, 644, 5, 97, 0, 0, 644, 645, 5, 116, 0, 0, 645, 646, 5, 97, 0, 0, 646, 647, 5, 115, 0, 0, 647, 648, 5, 105, 0, 0, 648, 650, 5, 103, 0, 0, 649, 620, 1, 0, 0, 0, 649, 623, 1, 0, 0, 0, 649, 627, 1, 0, 0, 0, 649, 633, 1, 0, 0, 0, 649, 639, 1, 0, 0, 0, 649, 642, 1, 0, 0, 0, 650, 128, 1, 0, 0, 0, 651, 652, 5, 98, 0, 0, 652, 653, 5, 121, 0, 0, 653, 654, 5, 116, 0, 0, 654, 655, 5, 101, 0, 0, 655, 656, 5, 115, 0, 0, 656, 130, 1, 0, 0, 0, 657, 658, 5, 98, 0, 0, 658, 659, 5, 121, 0, 0, 659, 660, 5, 116, 0, 0, 660, 661, 5, 101, 0, 0, 661, 662, 5, 115, 0, 0, 662, 663, 1, 0, 0, 0, 663, 669, 3, 133, 66, 0, 664, 665, 5, 98, 0, 0, 665, 666, 5, 121, 0, 0, 666, 667, 5, 116, 0, 0, 667, 669, 5, 101, 0, 0, 668, 657, 1, 0, 0, 0, 668, 664, 1, 0, 0, 0, 669, 132, 1, 0, 0, 0, 670, 674, 7, 2, 0, 0, 671, 673, 7, 0, 0, 0, 672, 671, 1, 0, 0, 0, 673, 676, 1, 0, 0, 0, 674, 672, 1, 0, 0, 0, 674, 675, 1, 0, 0, 0, 675, 134, 1, 0, 0, 0, 676, 674, 1, 0, 0, 0, 677, 683, 5, 34, 0, 0, 678, 679, 5, 92, 0, 0, 679, 682, 5, 34, 0, 0, 680, 682, 8, 3, 0, 0, 681, 678, 1, 0, 0, 0, 681, 680, 1, 0, 0, 0, 682, 685, 1, 0, 0, 0, 683, 684, 1, 0, 0, 0, 683, 681, 1, 0, 0, 0, 684, 686, 1, 0, 0, 0, 685, 683, 1, 0, 0, 0, 686, 698, 5, 34, 0, 0, 687, 693, 5, 39, 0, 0, 688, 689, 5, 92, 0, 0, 689, 692, 5, 39, 0, 0, 690, 692, 8, 4, 0, 0, 691, 688, 1, 0, 0, 0, 691, 690, 1, 0, 0, 0, 692, 695, 1, 0, 0, 0, 693, 694, 1, 0, 0, 0, 693, 691, 1, 0, 0, 0, 694, 696, 1, 0, 0, 0, 695, 693, 1, 0, 0, 0, 696, 698, 5, 39, 0, 0, 697, 677, 1, 0, 0, 0, 697, 687, 1, 0, 0, 0, 698, 136, 1, 0, 0, 0, 699, 700, 5, 100, 0, 0, 700, 701, 5, 97, 0, 0, 701, 702, 5, 116, 0, 0, 702, 703, 5, 101, 0, 0, 703, 704, 5, 40, 0, 0, 704, 705, 1, 0, 0, 0, 705, 706, 3, 135, 67, 0, 706, 707, 5, 41, 0, 0, 707, 138, 1, 0, 0, 0, 708, 709, 5, 48, 0, 0, 709, 713, 7, 5, 0, 0, 710, 712, 7, 6, 0, 0, 711, 710, 1, 0, 0, 0, 712, 715, 1, 0, 0, 0, 713, 711, 1, 0, 0, 0, 713, 714, 1, 0, 0, 0, 714, 140, 1, 0, 0, 0, 715, 713, 1, 0, 0, 0, 716, 717, 5, 116, 0, 0, 717, 718, 5, 104, 0, 0, 718, 719, 5, 105, 0, 0, 719, 720, 5, 115, 0, 0, 720, 721, 5, 46, 0, 0, 721, 722, 5, 97, 0, 0, 722, 723, 5, 103, 0, 0, 723, 732, 5, 101, 0, 0, 724, 725, 5, 116, 0, 0, 725, 726, 5, 120, 0, 0, 726, 727, 5, 46, 0, 0, 727, 728, 5, 116, 0, 0, 728, 729, 5, 105, 0, 0, 729, 730, 5, 109, 0, 0, 730, 732, 5, 101, 0, 0, 731, 716, 1, 0, 0, 0, 731, 724, 1, 0, 0, 0, 732, 142, 1, 0, 0, 0, 733, 734, 5, 117, 0, 0, 734, 735, 5, 110, 0, 0, 735, 736, 5, 115, 0, 0, 736, 737, 5, 97, 0, 0, 737, 738, 5, 102, 0, 0, 738, 739, 5, 101, 0, 0, 739, 740, 5, 95, 0, 0, 740, 741, 5, 105, 0, 0, 741, 742, 5, 110, 0, 0, 742, 782, 5, 116, 0, 0, 743, 744, 5, 117, 0, 0, 744, 745, 5, 110, 0, 0, 745, 746, 5, 115, 0, 0, 746, 747, 5, 97, 0, 0, 747, 748, 5, 102, 0, 0, 748, 749, 5, 101, 0, 0, 749, 750, 5, 95, 0, 0, 750, 751, 5, 98, 0, 0, 751, 752, 5, 111, 0, 0, 752, 753, 5, 111, 0, 0, 753, 782, 5, 108, 0, 0, 754, 755, 5, 117, 0, 0, 755, 756, 5, 110, 0, 0, 756, 757, 5, 115, 0, 0, 757, 758, 5, 97, 0, 0, 758, 759, 5, 102, 0, 0, 759, 760, 5, 101, 0, 0, 760, 761, 5, 95, 0, 0, 761, 762, 5, 98, 0, 0, 762, 763, 5, 121, 0, 0, 763, 764, 5, 116, 0, 0, 764, 765, 5, 101, 0, 0, 765, 766, 5, 115, 0, 0, 766, 768, 1, 0, 0, 0, 767, 769, 3, 133, 66, 0, 768, 767, 1, 0, 0, 0, 768, 769, 1, 0, 0, 0, 769, 782, 1, 0, 0, 0, 770, 771, 5, 117, 0, 0, 771, 772, 5, 110, 0, 0, 772, 773, 5, 115, 0, 0, 773, 774, 5, 97, 0, 0, 774, 775, 5, 102, 0, 0, 775, 776, 5, 101, 0, 0, 776, 777, 5, 95, 0, 0, 777, 778, 5, 98, 0, 0, 778, 779, 5, 121, 0, 0, 779, 780, 5, 116, 0, 0, 780, 782, 5, 101, 0, 0, 781, 733, 1, 0, 0, 0, 781, 743, 1, 0, 0, 0, 781, 754, 1, 0, 0, 0, 781, 770, 1, 0, 0, 0, 782, 144, 1, 0, 0, 0, 783, 784, 5, 116, 0, 0, 784, 785, 5, 104, 0, 0, 785, 786, 5, 105, 0, 0, 786, 787, 5, 115, 0, 0, 787, 788, 5, 46, 0, 0, 788, 789, 5, 97, 0, 0, 789, 790, 5, 99, 0, 0, 790, 791, 5, 116, 0, 0, 791, 792, 5, 105, 0, 0, 792, 793, 5, 118, 0, 0, 793, 794, 5, 101, 0, 0, 794, 795, 5, 73, 0, 0, 795, 796, 5, 110, 0, 0, 796, 797, 5, 112, 0, 0, 797, 798, 5, 117, 0, 0, 798, 799, 5, 116, 0, 0, 799, 800, 5, 73, 0, 0, 800, 801, 5, 110, 0, 0, 801, 802, 5, 100, 0, 0, 802, 803, 5, 101, 0, 0, 803, 878, 5, 120, 0, 0, 804, 805, 5, 116, 0, 0, 805, 806, 5, 104, 0, 0, 806, 807, 5, 105, 0, 0, 807, 808, 5, 115, 0, 0, 808, 809, 5, 46, 0, 0, 809, 810, 5, 97, 0, 0, 810, 811, 5, 99, 0, 0, 811, 812, 5, 116, 0, 0, 812, 813, 5, 105, 0, 0, 813, 814, 5, 118, 0, 0, 814, 815, 5, 101, 0, 0, 815, 816, 5, 66, 0, 0, 816, 817, 5, 121, 0, 0, 817, 818, 5, 116, 0, 0, 818, 819, 5, 101, 0, 0, 819, 820, 5, 99, 0, 0, 820, 821, 5, 111, 0, 0, 821, 822, 5, 100, 0, 0, 822, 878, 5, 101, 0, 0, 823, 824, 5, 116, 0, 0, 824, 825, 5, 120, 0, 0, 825, 826, 5, 46, 0, 0, 826, 827, 5, 105, 0, 0, 827, 828, 5, 110, 0, 0, 828, 829, 5, 112, 0, 0, 829, 830, 5, 117, 0, 0, 830, 831, 5, 116, 0, 0, 831, 832, 5, 115, 0, 0, 832, 833, 5, 46, 0, 0, 833, 834, 5, 108, 0, 0, 834, 835, 5, 101, 0, 0, 835, 836, 5, 110, 0, 0, 836, 837, 5, 103, 0, 0, 837, 838, 5, 116, 0, 0, 838, 878, 5, 104, 0, 0, 839, 840, 5, 116, 0, 0, 840, 841, 5, 120, 0, 0, 841, 842, 5, 46, 0, 0, 842, 843, 5, 111, 0, 0, 843, 844, 5, 117, 0, 0, 844, 845, 5, 116, 0, 0, 845, 846, 5, 112, 0, 0, 846, 847, 5, 117, 0, 0, 847, 848, 5, 116, 0, 0, 848, 849, 5, 115, 0, 0, 849, 850, 5, 46, 0, 0, 850, 851, 5, 108, 0, 0, 851, 852, 5, 101, 0, 0, 852, 853, 5, 110, 0, 0, 853, 854, 5, 103, 0, 0, 854, 855, 5, 116, 0, 0, 855, 878, 5, 104, 0, 0, 856, 857, 5, 116, 0, 0, 857, 858, 5, 120, 0, 0, 858, 859, 5, 46, 0, 0, 859, 860, 5, 118, 0, 0, 860, 861, 5, 101, 0, 0, 861, 862, 5, 114, 0, 0, 862, 863, 5, 115, 0, 0, 863, 864, 5, 105, 0, 0, 864, 865, 5, 111, 0, 0, 865, 878, 5, 110, 0, 0, 866, 867, 5, 116, 0, 0, 867, 868, 5, 120, 0, 0, 868, 869, 5, 46, 0, 0, 869, 870, 5, 108, 0, 0, 870, 871, 5, 111, 0, 0, 871, 872, 5, 99, 0, 0, 872, 873, 5, 107, 0, 0, 873, 874, 5, 116, 0, 0, 874, 875, 5, 105, 0, 0, 875, 876, 5, 109, 0, 0, 876, 878, 5, 101, 0, 0, 877, 783, 1, 0, 0, 0, 877, 804, 1, 0, 0, 0, 877, 823, 1, 0, 0, 0, 877, 839, 1, 0, 0, 0, 877, 856, 1, 0, 0, 0, 877, 866, 1, 0, 0, 0, 878, 146, 1, 0, 0, 0, 879, 883, 7, 7, 0, 0, 880, 882, 7, 8, 0, 0, 881, 880, 1, 0, 0, 0, 882, 885, 1, 0, 0, 0, 883, 881, 1, 0, 0, 0, 883, 884, 1, 0, 0, 0, 884, 148, 1, 0, 0, 0, 885, 883, 1, 0, 0, 0, 886, 888, 7, 9, 0, 0, 887, 886, 1, 0, 0, 0, 888, 889, 1, 0, 0, 0, 889, 887, 1, 0, 0, 0, 889, 890, 1, 0, 0, 0, 890, 891, 1, 0, 0, 0, 891, 892, 6, 74, 0, 0, 892, 150, 1, 0, 0, 0, 893, 894, 5, 47, 0, 0, 894, 895, 5, 42, 0, 0, 895, 899, 1, 0, 0, 0, 896, 898, 9, 0, 0, 0, 897, 896, 1, 0, 0, 0, 898, 901, 1, 0, 0, 0, 899, 900, 1, 0, 0, 0, 899, 897, 1, 0, 0, 0, 900, 902, 1, 0, 0, 0, 901, 899, 1, 0, 0, 0, 902, 903, 5, 42, 0, 0, 903, 904, 5, 47, 0, 0, 904, 905, 1, 0, 0, 0, 905, 906, 6, 75, 1, 0, 906, 152, 1, 0, 0, 0, 907, 908, 5, 47, 0, 0, 908, 909, 5, 47, 0, 0, 909, 913, 1, 0, 0, 0, 910, 912, 8, 10, 0, 0, 911, 910, 1, 0, 0, 0, 912, 915, 1, 0, 0, 0, 913, 911, 1, 0, 0, 0, 913, 914, 1, 0, 0, 0, 914, 916, 1, 0, 0, 0, 915, 913, 1, 0, 0, 0, 916, 917, 6, 76, 1, 0, 917, 154, 1, 0, 0, 0, 28, 0, 510, 516, 522, 533, 592, 595, 599, 604, 610, 614, 649, 668, 674, 681, 683, 691, 693, 697, 713, 731, 768, 781, 877, 883, 889, 899, 913, 2, 6, 0, 0, 0, 1, 0] \ No newline at end of file +[4, 0, 78, 928, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7, 41, 2, 42, 7, 42, 2, 43, 7, 43, 2, 44, 7, 44, 2, 45, 7, 45, 2, 46, 7, 46, 2, 47, 7, 47, 2, 48, 7, 48, 2, 49, 7, 49, 2, 50, 7, 50, 2, 51, 7, 51, 2, 52, 7, 52, 2, 53, 7, 53, 2, 54, 7, 54, 2, 55, 7, 55, 2, 56, 7, 56, 2, 57, 7, 57, 2, 58, 7, 58, 2, 59, 7, 59, 2, 60, 7, 60, 2, 61, 7, 61, 2, 62, 7, 62, 2, 63, 7, 63, 2, 64, 7, 64, 2, 65, 7, 65, 2, 66, 7, 66, 2, 67, 7, 67, 2, 68, 7, 68, 2, 69, 7, 69, 2, 70, 7, 70, 2, 71, 7, 71, 2, 72, 7, 72, 2, 73, 7, 73, 2, 74, 7, 74, 2, 75, 7, 75, 2, 76, 7, 76, 2, 77, 7, 77, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 3, 1, 3, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 1, 6, 1, 6, 1, 7, 1, 7, 1, 8, 1, 8, 1, 8, 1, 9, 1, 9, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 11, 1, 11, 1, 12, 1, 12, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 15, 1, 15, 1, 16, 1, 16, 1, 17, 1, 17, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 20, 1, 20, 1, 20, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 22, 1, 22, 1, 22, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 1, 24, 1, 24, 1, 24, 1, 24, 1, 25, 1, 25, 1, 25, 1, 25, 1, 26, 1, 26, 1, 27, 1, 27, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 43, 1, 43, 1, 44, 1, 44, 1, 45, 1, 45, 1, 46, 1, 46, 1, 47, 1, 47, 1, 48, 1, 48, 1, 49, 1, 49, 1, 49, 1, 50, 1, 50, 1, 50, 1, 51, 1, 51, 1, 51, 1, 52, 1, 52, 1, 52, 1, 53, 1, 53, 1, 54, 1, 54, 1, 55, 1, 55, 1, 55, 1, 56, 1, 56, 1, 56, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 58, 4, 58, 519, 8, 58, 11, 58, 12, 58, 520, 1, 58, 1, 58, 4, 58, 525, 8, 58, 11, 58, 12, 58, 526, 1, 58, 1, 58, 4, 58, 531, 8, 58, 11, 58, 12, 58, 532, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 544, 8, 59, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 3, 60, 603, 8, 60, 1, 61, 3, 61, 606, 8, 61, 1, 61, 1, 61, 3, 61, 610, 8, 61, 1, 62, 4, 62, 613, 8, 62, 11, 62, 12, 62, 614, 1, 62, 1, 62, 4, 62, 619, 8, 62, 11, 62, 12, 62, 620, 5, 62, 623, 8, 62, 10, 62, 12, 62, 626, 9, 62, 1, 63, 1, 63, 1, 63, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 3, 64, 660, 8, 64, 1, 65, 1, 65, 1, 65, 1, 65, 1, 65, 1, 65, 1, 66, 1, 66, 1, 66, 1, 66, 1, 66, 1, 66, 1, 66, 1, 66, 1, 66, 1, 66, 1, 66, 3, 66, 679, 8, 66, 1, 67, 1, 67, 5, 67, 683, 8, 67, 10, 67, 12, 67, 686, 9, 67, 1, 68, 1, 68, 1, 68, 1, 68, 5, 68, 692, 8, 68, 10, 68, 12, 68, 695, 9, 68, 1, 68, 1, 68, 1, 68, 1, 68, 1, 68, 5, 68, 702, 8, 68, 10, 68, 12, 68, 705, 9, 68, 1, 68, 3, 68, 708, 8, 68, 1, 69, 1, 69, 1, 69, 1, 69, 1, 69, 1, 69, 1, 69, 1, 69, 1, 69, 1, 70, 1, 70, 1, 70, 5, 70, 722, 8, 70, 10, 70, 12, 70, 725, 9, 70, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 3, 71, 742, 8, 71, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 3, 72, 779, 8, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 3, 72, 792, 8, 72, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 3, 73, 888, 8, 73, 1, 74, 1, 74, 5, 74, 892, 8, 74, 10, 74, 12, 74, 895, 9, 74, 1, 75, 4, 75, 898, 8, 75, 11, 75, 12, 75, 899, 1, 75, 1, 75, 1, 76, 1, 76, 1, 76, 1, 76, 5, 76, 908, 8, 76, 10, 76, 12, 76, 911, 9, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 77, 1, 77, 1, 77, 1, 77, 5, 77, 922, 8, 77, 10, 77, 12, 77, 925, 9, 77, 1, 77, 1, 77, 3, 693, 703, 909, 0, 78, 1, 1, 3, 2, 5, 3, 7, 4, 9, 5, 11, 6, 13, 7, 15, 8, 17, 9, 19, 10, 21, 11, 23, 12, 25, 13, 27, 14, 29, 15, 31, 16, 33, 17, 35, 18, 37, 19, 39, 20, 41, 21, 43, 22, 45, 23, 47, 24, 49, 25, 51, 26, 53, 27, 55, 28, 57, 29, 59, 30, 61, 31, 63, 32, 65, 33, 67, 34, 69, 35, 71, 36, 73, 37, 75, 38, 77, 39, 79, 40, 81, 41, 83, 42, 85, 43, 87, 44, 89, 45, 91, 46, 93, 47, 95, 48, 97, 49, 99, 50, 101, 51, 103, 52, 105, 53, 107, 54, 109, 55, 111, 56, 113, 57, 115, 58, 117, 59, 119, 60, 121, 61, 123, 62, 125, 63, 127, 64, 129, 65, 131, 66, 133, 67, 135, 68, 137, 69, 139, 70, 141, 71, 143, 72, 145, 73, 147, 74, 149, 75, 151, 76, 153, 77, 155, 78, 1, 0, 11, 1, 0, 48, 57, 2, 0, 69, 69, 101, 101, 1, 0, 49, 57, 3, 0, 10, 10, 13, 13, 34, 34, 3, 0, 10, 10, 13, 13, 39, 39, 2, 0, 88, 88, 120, 120, 3, 0, 48, 57, 65, 70, 97, 102, 2, 0, 65, 90, 97, 122, 4, 0, 48, 57, 65, 90, 95, 95, 97, 122, 3, 0, 9, 10, 12, 13, 32, 32, 2, 0, 10, 10, 13, 13, 972, 0, 1, 1, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 5, 1, 0, 0, 0, 0, 7, 1, 0, 0, 0, 0, 9, 1, 0, 0, 0, 0, 11, 1, 0, 0, 0, 0, 13, 1, 0, 0, 0, 0, 15, 1, 0, 0, 0, 0, 17, 1, 0, 0, 0, 0, 19, 1, 0, 0, 0, 0, 21, 1, 0, 0, 0, 0, 23, 1, 0, 0, 0, 0, 25, 1, 0, 0, 0, 0, 27, 1, 0, 0, 0, 0, 29, 1, 0, 0, 0, 0, 31, 1, 0, 0, 0, 0, 33, 1, 0, 0, 0, 0, 35, 1, 0, 0, 0, 0, 37, 1, 0, 0, 0, 0, 39, 1, 0, 0, 0, 0, 41, 1, 0, 0, 0, 0, 43, 1, 0, 0, 0, 0, 45, 1, 0, 0, 0, 0, 47, 1, 0, 0, 0, 0, 49, 1, 0, 0, 0, 0, 51, 1, 0, 0, 0, 0, 53, 1, 0, 0, 0, 0, 55, 1, 0, 0, 0, 0, 57, 1, 0, 0, 0, 0, 59, 1, 0, 0, 0, 0, 61, 1, 0, 0, 0, 0, 63, 1, 0, 0, 0, 0, 65, 1, 0, 0, 0, 0, 67, 1, 0, 0, 0, 0, 69, 1, 0, 0, 0, 0, 71, 1, 0, 0, 0, 0, 73, 1, 0, 0, 0, 0, 75, 1, 0, 0, 0, 0, 77, 1, 0, 0, 0, 0, 79, 1, 0, 0, 0, 0, 81, 1, 0, 0, 0, 0, 83, 1, 0, 0, 0, 0, 85, 1, 0, 0, 0, 0, 87, 1, 0, 0, 0, 0, 89, 1, 0, 0, 0, 0, 91, 1, 0, 0, 0, 0, 93, 1, 0, 0, 0, 0, 95, 1, 0, 0, 0, 0, 97, 1, 0, 0, 0, 0, 99, 1, 0, 0, 0, 0, 101, 1, 0, 0, 0, 0, 103, 1, 0, 0, 0, 0, 105, 1, 0, 0, 0, 0, 107, 1, 0, 0, 0, 0, 109, 1, 0, 0, 0, 0, 111, 1, 0, 0, 0, 0, 113, 1, 0, 0, 0, 0, 115, 1, 0, 0, 0, 0, 117, 1, 0, 0, 0, 0, 119, 1, 0, 0, 0, 0, 121, 1, 0, 0, 0, 0, 123, 1, 0, 0, 0, 0, 125, 1, 0, 0, 0, 0, 127, 1, 0, 0, 0, 0, 129, 1, 0, 0, 0, 0, 131, 1, 0, 0, 0, 0, 133, 1, 0, 0, 0, 0, 135, 1, 0, 0, 0, 0, 137, 1, 0, 0, 0, 0, 139, 1, 0, 0, 0, 0, 141, 1, 0, 0, 0, 0, 143, 1, 0, 0, 0, 0, 145, 1, 0, 0, 0, 0, 147, 1, 0, 0, 0, 0, 149, 1, 0, 0, 0, 0, 151, 1, 0, 0, 0, 0, 153, 1, 0, 0, 0, 0, 155, 1, 0, 0, 0, 1, 157, 1, 0, 0, 0, 3, 164, 1, 0, 0, 0, 5, 166, 1, 0, 0, 0, 7, 177, 1, 0, 0, 0, 9, 179, 1, 0, 0, 0, 11, 181, 1, 0, 0, 0, 13, 184, 1, 0, 0, 0, 15, 186, 1, 0, 0, 0, 17, 188, 1, 0, 0, 0, 19, 191, 1, 0, 0, 0, 21, 193, 1, 0, 0, 0, 23, 202, 1, 0, 0, 0, 25, 204, 1, 0, 0, 0, 27, 206, 1, 0, 0, 0, 29, 214, 1, 0, 0, 0, 31, 223, 1, 0, 0, 0, 33, 225, 1, 0, 0, 0, 35, 227, 1, 0, 0, 0, 37, 229, 1, 0, 0, 0, 39, 237, 1, 0, 0, 0, 41, 249, 1, 0, 0, 0, 43, 252, 1, 0, 0, 0, 45, 257, 1, 0, 0, 0, 47, 260, 1, 0, 0, 0, 49, 266, 1, 0, 0, 0, 51, 270, 1, 0, 0, 0, 53, 274, 1, 0, 0, 0, 55, 276, 1, 0, 0, 0, 57, 278, 1, 0, 0, 0, 59, 289, 1, 0, 0, 0, 61, 296, 1, 0, 0, 0, 63, 313, 1, 0, 0, 0, 65, 328, 1, 0, 0, 0, 67, 343, 1, 0, 0, 0, 69, 356, 1, 0, 0, 0, 71, 366, 1, 0, 0, 0, 73, 391, 1, 0, 0, 0, 75, 406, 1, 0, 0, 0, 77, 425, 1, 0, 0, 0, 79, 441, 1, 0, 0, 0, 81, 452, 1, 0, 0, 0, 83, 460, 1, 0, 0, 0, 85, 467, 1, 0, 0, 0, 87, 474, 1, 0, 0, 0, 89, 476, 1, 0, 0, 0, 91, 478, 1, 0, 0, 0, 93, 480, 1, 0, 0, 0, 95, 482, 1, 0, 0, 0, 97, 484, 1, 0, 0, 0, 99, 486, 1, 0, 0, 0, 101, 489, 1, 0, 0, 0, 103, 492, 1, 0, 0, 0, 105, 495, 1, 0, 0, 0, 107, 498, 1, 0, 0, 0, 109, 500, 1, 0, 0, 0, 111, 502, 1, 0, 0, 0, 113, 505, 1, 0, 0, 0, 115, 508, 1, 0, 0, 0, 117, 518, 1, 0, 0, 0, 119, 543, 1, 0, 0, 0, 121, 602, 1, 0, 0, 0, 123, 605, 1, 0, 0, 0, 125, 612, 1, 0, 0, 0, 127, 627, 1, 0, 0, 0, 129, 659, 1, 0, 0, 0, 131, 661, 1, 0, 0, 0, 133, 678, 1, 0, 0, 0, 135, 680, 1, 0, 0, 0, 137, 707, 1, 0, 0, 0, 139, 709, 1, 0, 0, 0, 141, 718, 1, 0, 0, 0, 143, 741, 1, 0, 0, 0, 145, 791, 1, 0, 0, 0, 147, 887, 1, 0, 0, 0, 149, 889, 1, 0, 0, 0, 151, 897, 1, 0, 0, 0, 153, 903, 1, 0, 0, 0, 155, 917, 1, 0, 0, 0, 157, 158, 5, 112, 0, 0, 158, 159, 5, 114, 0, 0, 159, 160, 5, 97, 0, 0, 160, 161, 5, 103, 0, 0, 161, 162, 5, 109, 0, 0, 162, 163, 5, 97, 0, 0, 163, 2, 1, 0, 0, 0, 164, 165, 5, 59, 0, 0, 165, 4, 1, 0, 0, 0, 166, 167, 5, 99, 0, 0, 167, 168, 5, 97, 0, 0, 168, 169, 5, 115, 0, 0, 169, 170, 5, 104, 0, 0, 170, 171, 5, 115, 0, 0, 171, 172, 5, 99, 0, 0, 172, 173, 5, 114, 0, 0, 173, 174, 5, 105, 0, 0, 174, 175, 5, 112, 0, 0, 175, 176, 5, 116, 0, 0, 176, 6, 1, 0, 0, 0, 177, 178, 5, 94, 0, 0, 178, 8, 1, 0, 0, 0, 179, 180, 5, 126, 0, 0, 180, 10, 1, 0, 0, 0, 181, 182, 5, 62, 0, 0, 182, 183, 5, 61, 0, 0, 183, 12, 1, 0, 0, 0, 184, 185, 5, 62, 0, 0, 185, 14, 1, 0, 0, 0, 186, 187, 5, 60, 0, 0, 187, 16, 1, 0, 0, 0, 188, 189, 5, 60, 0, 0, 189, 190, 5, 61, 0, 0, 190, 18, 1, 0, 0, 0, 191, 192, 5, 61, 0, 0, 192, 20, 1, 0, 0, 0, 193, 194, 5, 99, 0, 0, 194, 195, 5, 111, 0, 0, 195, 196, 5, 110, 0, 0, 196, 197, 5, 116, 0, 0, 197, 198, 5, 114, 0, 0, 198, 199, 5, 97, 0, 0, 199, 200, 5, 99, 0, 0, 200, 201, 5, 116, 0, 0, 201, 22, 1, 0, 0, 0, 202, 203, 5, 123, 0, 0, 203, 24, 1, 0, 0, 0, 204, 205, 5, 125, 0, 0, 205, 26, 1, 0, 0, 0, 206, 207, 5, 108, 0, 0, 207, 208, 5, 105, 0, 0, 208, 209, 5, 98, 0, 0, 209, 210, 5, 114, 0, 0, 210, 211, 5, 97, 0, 0, 211, 212, 5, 114, 0, 0, 212, 213, 5, 121, 0, 0, 213, 28, 1, 0, 0, 0, 214, 215, 5, 102, 0, 0, 215, 216, 5, 117, 0, 0, 216, 217, 5, 110, 0, 0, 217, 218, 5, 99, 0, 0, 218, 219, 5, 116, 0, 0, 219, 220, 5, 105, 0, 0, 220, 221, 5, 111, 0, 0, 221, 222, 5, 110, 0, 0, 222, 30, 1, 0, 0, 0, 223, 224, 5, 40, 0, 0, 224, 32, 1, 0, 0, 0, 225, 226, 5, 44, 0, 0, 226, 34, 1, 0, 0, 0, 227, 228, 5, 41, 0, 0, 228, 36, 1, 0, 0, 0, 229, 230, 5, 114, 0, 0, 230, 231, 5, 101, 0, 0, 231, 232, 5, 113, 0, 0, 232, 233, 5, 117, 0, 0, 233, 234, 5, 105, 0, 0, 234, 235, 5, 114, 0, 0, 235, 236, 5, 101, 0, 0, 236, 38, 1, 0, 0, 0, 237, 238, 5, 99, 0, 0, 238, 239, 5, 111, 0, 0, 239, 240, 5, 110, 0, 0, 240, 241, 5, 115, 0, 0, 241, 242, 5, 111, 0, 0, 242, 243, 5, 108, 0, 0, 243, 244, 5, 101, 0, 0, 244, 245, 5, 46, 0, 0, 245, 246, 5, 108, 0, 0, 246, 247, 5, 111, 0, 0, 247, 248, 5, 103, 0, 0, 248, 40, 1, 0, 0, 0, 249, 250, 5, 105, 0, 0, 250, 251, 5, 102, 0, 0, 251, 42, 1, 0, 0, 0, 252, 253, 5, 101, 0, 0, 253, 254, 5, 108, 0, 0, 254, 255, 5, 115, 0, 0, 255, 256, 5, 101, 0, 0, 256, 44, 1, 0, 0, 0, 257, 258, 5, 100, 0, 0, 258, 259, 5, 111, 0, 0, 259, 46, 1, 0, 0, 0, 260, 261, 5, 119, 0, 0, 261, 262, 5, 104, 0, 0, 262, 263, 5, 105, 0, 0, 263, 264, 5, 108, 0, 0, 264, 265, 5, 101, 0, 0, 265, 48, 1, 0, 0, 0, 266, 267, 5, 102, 0, 0, 267, 268, 5, 111, 0, 0, 268, 269, 5, 114, 0, 0, 269, 50, 1, 0, 0, 0, 270, 271, 5, 110, 0, 0, 271, 272, 5, 101, 0, 0, 272, 273, 5, 119, 0, 0, 273, 52, 1, 0, 0, 0, 274, 275, 5, 91, 0, 0, 275, 54, 1, 0, 0, 0, 276, 277, 5, 93, 0, 0, 277, 56, 1, 0, 0, 0, 278, 279, 5, 116, 0, 0, 279, 280, 5, 120, 0, 0, 280, 281, 5, 46, 0, 0, 281, 282, 5, 111, 0, 0, 282, 283, 5, 117, 0, 0, 283, 284, 5, 116, 0, 0, 284, 285, 5, 112, 0, 0, 285, 286, 5, 117, 0, 0, 286, 287, 5, 116, 0, 0, 287, 288, 5, 115, 0, 0, 288, 58, 1, 0, 0, 0, 289, 290, 5, 46, 0, 0, 290, 291, 5, 118, 0, 0, 291, 292, 5, 97, 0, 0, 292, 293, 5, 108, 0, 0, 293, 294, 5, 117, 0, 0, 294, 295, 5, 101, 0, 0, 295, 60, 1, 0, 0, 0, 296, 297, 5, 46, 0, 0, 297, 298, 5, 108, 0, 0, 298, 299, 5, 111, 0, 0, 299, 300, 5, 99, 0, 0, 300, 301, 5, 107, 0, 0, 301, 302, 5, 105, 0, 0, 302, 303, 5, 110, 0, 0, 303, 304, 5, 103, 0, 0, 304, 305, 5, 66, 0, 0, 305, 306, 5, 121, 0, 0, 306, 307, 5, 116, 0, 0, 307, 308, 5, 101, 0, 0, 308, 309, 5, 99, 0, 0, 309, 310, 5, 111, 0, 0, 310, 311, 5, 100, 0, 0, 311, 312, 5, 101, 0, 0, 312, 62, 1, 0, 0, 0, 313, 314, 5, 46, 0, 0, 314, 315, 5, 116, 0, 0, 315, 316, 5, 111, 0, 0, 316, 317, 5, 107, 0, 0, 317, 318, 5, 101, 0, 0, 318, 319, 5, 110, 0, 0, 319, 320, 5, 67, 0, 0, 320, 321, 5, 97, 0, 0, 321, 322, 5, 116, 0, 0, 322, 323, 5, 101, 0, 0, 323, 324, 5, 103, 0, 0, 324, 325, 5, 111, 0, 0, 325, 326, 5, 114, 0, 0, 326, 327, 5, 121, 0, 0, 327, 64, 1, 0, 0, 0, 328, 329, 5, 46, 0, 0, 329, 330, 5, 110, 0, 0, 330, 331, 5, 102, 0, 0, 331, 332, 5, 116, 0, 0, 332, 333, 5, 67, 0, 0, 333, 334, 5, 111, 0, 0, 334, 335, 5, 109, 0, 0, 335, 336, 5, 109, 0, 0, 336, 337, 5, 105, 0, 0, 337, 338, 5, 116, 0, 0, 338, 339, 5, 109, 0, 0, 339, 340, 5, 101, 0, 0, 340, 341, 5, 110, 0, 0, 341, 342, 5, 116, 0, 0, 342, 66, 1, 0, 0, 0, 343, 344, 5, 46, 0, 0, 344, 345, 5, 116, 0, 0, 345, 346, 5, 111, 0, 0, 346, 347, 5, 107, 0, 0, 347, 348, 5, 101, 0, 0, 348, 349, 5, 110, 0, 0, 349, 350, 5, 65, 0, 0, 350, 351, 5, 109, 0, 0, 351, 352, 5, 111, 0, 0, 352, 353, 5, 117, 0, 0, 353, 354, 5, 110, 0, 0, 354, 355, 5, 116, 0, 0, 355, 68, 1, 0, 0, 0, 356, 357, 5, 116, 0, 0, 357, 358, 5, 120, 0, 0, 358, 359, 5, 46, 0, 0, 359, 360, 5, 105, 0, 0, 360, 361, 5, 110, 0, 0, 361, 362, 5, 112, 0, 0, 362, 363, 5, 117, 0, 0, 363, 364, 5, 116, 0, 0, 364, 365, 5, 115, 0, 0, 365, 70, 1, 0, 0, 0, 366, 367, 5, 46, 0, 0, 367, 368, 5, 111, 0, 0, 368, 369, 5, 117, 0, 0, 369, 370, 5, 116, 0, 0, 370, 371, 5, 112, 0, 0, 371, 372, 5, 111, 0, 0, 372, 373, 5, 105, 0, 0, 373, 374, 5, 110, 0, 0, 374, 375, 5, 116, 0, 0, 375, 376, 5, 84, 0, 0, 376, 377, 5, 114, 0, 0, 377, 378, 5, 97, 0, 0, 378, 379, 5, 110, 0, 0, 379, 380, 5, 115, 0, 0, 380, 381, 5, 97, 0, 0, 381, 382, 5, 99, 0, 0, 382, 383, 5, 116, 0, 0, 383, 384, 5, 105, 0, 0, 384, 385, 5, 111, 0, 0, 385, 386, 5, 110, 0, 0, 386, 387, 5, 72, 0, 0, 387, 388, 5, 97, 0, 0, 388, 389, 5, 115, 0, 0, 389, 390, 5, 104, 0, 0, 390, 72, 1, 0, 0, 0, 391, 392, 5, 46, 0, 0, 392, 393, 5, 111, 0, 0, 393, 394, 5, 117, 0, 0, 394, 395, 5, 116, 0, 0, 395, 396, 5, 112, 0, 0, 396, 397, 5, 111, 0, 0, 397, 398, 5, 105, 0, 0, 398, 399, 5, 110, 0, 0, 399, 400, 5, 116, 0, 0, 400, 401, 5, 73, 0, 0, 401, 402, 5, 110, 0, 0, 402, 403, 5, 100, 0, 0, 403, 404, 5, 101, 0, 0, 404, 405, 5, 120, 0, 0, 405, 74, 1, 0, 0, 0, 406, 407, 5, 46, 0, 0, 407, 408, 5, 117, 0, 0, 408, 409, 5, 110, 0, 0, 409, 410, 5, 108, 0, 0, 410, 411, 5, 111, 0, 0, 411, 412, 5, 99, 0, 0, 412, 413, 5, 107, 0, 0, 413, 414, 5, 105, 0, 0, 414, 415, 5, 110, 0, 0, 415, 416, 5, 103, 0, 0, 416, 417, 5, 66, 0, 0, 417, 418, 5, 121, 0, 0, 418, 419, 5, 116, 0, 0, 419, 420, 5, 101, 0, 0, 420, 421, 5, 99, 0, 0, 421, 422, 5, 111, 0, 0, 422, 423, 5, 100, 0, 0, 423, 424, 5, 101, 0, 0, 424, 76, 1, 0, 0, 0, 425, 426, 5, 46, 0, 0, 426, 427, 5, 115, 0, 0, 427, 428, 5, 101, 0, 0, 428, 429, 5, 113, 0, 0, 429, 430, 5, 117, 0, 0, 430, 431, 5, 101, 0, 0, 431, 432, 5, 110, 0, 0, 432, 433, 5, 99, 0, 0, 433, 434, 5, 101, 0, 0, 434, 435, 5, 78, 0, 0, 435, 436, 5, 117, 0, 0, 436, 437, 5, 109, 0, 0, 437, 438, 5, 98, 0, 0, 438, 439, 5, 101, 0, 0, 439, 440, 5, 114, 0, 0, 440, 78, 1, 0, 0, 0, 441, 442, 5, 46, 0, 0, 442, 443, 5, 114, 0, 0, 443, 444, 5, 101, 0, 0, 444, 445, 5, 118, 0, 0, 445, 446, 5, 101, 0, 0, 446, 447, 5, 114, 0, 0, 447, 448, 5, 115, 0, 0, 448, 449, 5, 101, 0, 0, 449, 450, 5, 40, 0, 0, 450, 451, 5, 41, 0, 0, 451, 80, 1, 0, 0, 0, 452, 453, 5, 46, 0, 0, 453, 454, 5, 108, 0, 0, 454, 455, 5, 101, 0, 0, 455, 456, 5, 110, 0, 0, 456, 457, 5, 103, 0, 0, 457, 458, 5, 116, 0, 0, 458, 459, 5, 104, 0, 0, 459, 82, 1, 0, 0, 0, 460, 461, 5, 46, 0, 0, 461, 462, 5, 115, 0, 0, 462, 463, 5, 112, 0, 0, 463, 464, 5, 108, 0, 0, 464, 465, 5, 105, 0, 0, 465, 466, 5, 116, 0, 0, 466, 84, 1, 0, 0, 0, 467, 468, 5, 46, 0, 0, 468, 469, 5, 115, 0, 0, 469, 470, 5, 108, 0, 0, 470, 471, 5, 105, 0, 0, 471, 472, 5, 99, 0, 0, 472, 473, 5, 101, 0, 0, 473, 86, 1, 0, 0, 0, 474, 475, 5, 33, 0, 0, 475, 88, 1, 0, 0, 0, 476, 477, 5, 45, 0, 0, 477, 90, 1, 0, 0, 0, 478, 479, 5, 42, 0, 0, 479, 92, 1, 0, 0, 0, 480, 481, 5, 47, 0, 0, 481, 94, 1, 0, 0, 0, 482, 483, 5, 37, 0, 0, 483, 96, 1, 0, 0, 0, 484, 485, 5, 43, 0, 0, 485, 98, 1, 0, 0, 0, 486, 487, 5, 62, 0, 0, 487, 488, 5, 62, 0, 0, 488, 100, 1, 0, 0, 0, 489, 490, 5, 60, 0, 0, 490, 491, 5, 60, 0, 0, 491, 102, 1, 0, 0, 0, 492, 493, 5, 61, 0, 0, 493, 494, 5, 61, 0, 0, 494, 104, 1, 0, 0, 0, 495, 496, 5, 33, 0, 0, 496, 497, 5, 61, 0, 0, 497, 106, 1, 0, 0, 0, 498, 499, 5, 38, 0, 0, 499, 108, 1, 0, 0, 0, 500, 501, 5, 124, 0, 0, 501, 110, 1, 0, 0, 0, 502, 503, 5, 38, 0, 0, 503, 504, 5, 38, 0, 0, 504, 112, 1, 0, 0, 0, 505, 506, 5, 124, 0, 0, 506, 507, 5, 124, 0, 0, 507, 114, 1, 0, 0, 0, 508, 509, 5, 99, 0, 0, 509, 510, 5, 111, 0, 0, 510, 511, 5, 110, 0, 0, 511, 512, 5, 115, 0, 0, 512, 513, 5, 116, 0, 0, 513, 514, 5, 97, 0, 0, 514, 515, 5, 110, 0, 0, 515, 516, 5, 116, 0, 0, 516, 116, 1, 0, 0, 0, 517, 519, 7, 0, 0, 0, 518, 517, 1, 0, 0, 0, 519, 520, 1, 0, 0, 0, 520, 518, 1, 0, 0, 0, 520, 521, 1, 0, 0, 0, 521, 522, 1, 0, 0, 0, 522, 524, 5, 46, 0, 0, 523, 525, 7, 0, 0, 0, 524, 523, 1, 0, 0, 0, 525, 526, 1, 0, 0, 0, 526, 524, 1, 0, 0, 0, 526, 527, 1, 0, 0, 0, 527, 528, 1, 0, 0, 0, 528, 530, 5, 46, 0, 0, 529, 531, 7, 0, 0, 0, 530, 529, 1, 0, 0, 0, 531, 532, 1, 0, 0, 0, 532, 530, 1, 0, 0, 0, 532, 533, 1, 0, 0, 0, 533, 118, 1, 0, 0, 0, 534, 535, 5, 116, 0, 0, 535, 536, 5, 114, 0, 0, 536, 537, 5, 117, 0, 0, 537, 544, 5, 101, 0, 0, 538, 539, 5, 102, 0, 0, 539, 540, 5, 97, 0, 0, 540, 541, 5, 108, 0, 0, 541, 542, 5, 115, 0, 0, 542, 544, 5, 101, 0, 0, 543, 534, 1, 0, 0, 0, 543, 538, 1, 0, 0, 0, 544, 120, 1, 0, 0, 0, 545, 546, 5, 115, 0, 0, 546, 547, 5, 97, 0, 0, 547, 548, 5, 116, 0, 0, 548, 549, 5, 111, 0, 0, 549, 550, 5, 115, 0, 0, 550, 551, 5, 104, 0, 0, 551, 552, 5, 105, 0, 0, 552, 603, 5, 115, 0, 0, 553, 554, 5, 115, 0, 0, 554, 555, 5, 97, 0, 0, 555, 556, 5, 116, 0, 0, 556, 603, 5, 115, 0, 0, 557, 558, 5, 102, 0, 0, 558, 559, 5, 105, 0, 0, 559, 560, 5, 110, 0, 0, 560, 561, 5, 110, 0, 0, 561, 562, 5, 101, 0, 0, 562, 603, 5, 121, 0, 0, 563, 564, 5, 98, 0, 0, 564, 565, 5, 105, 0, 0, 565, 566, 5, 116, 0, 0, 566, 603, 5, 115, 0, 0, 567, 568, 5, 98, 0, 0, 568, 569, 5, 105, 0, 0, 569, 570, 5, 116, 0, 0, 570, 571, 5, 99, 0, 0, 571, 572, 5, 111, 0, 0, 572, 573, 5, 105, 0, 0, 573, 603, 5, 110, 0, 0, 574, 575, 5, 115, 0, 0, 575, 576, 5, 101, 0, 0, 576, 577, 5, 99, 0, 0, 577, 578, 5, 111, 0, 0, 578, 579, 5, 110, 0, 0, 579, 580, 5, 100, 0, 0, 580, 603, 5, 115, 0, 0, 581, 582, 5, 109, 0, 0, 582, 583, 5, 105, 0, 0, 583, 584, 5, 110, 0, 0, 584, 585, 5, 117, 0, 0, 585, 586, 5, 116, 0, 0, 586, 587, 5, 101, 0, 0, 587, 603, 5, 115, 0, 0, 588, 589, 5, 104, 0, 0, 589, 590, 5, 111, 0, 0, 590, 591, 5, 117, 0, 0, 591, 592, 5, 114, 0, 0, 592, 603, 5, 115, 0, 0, 593, 594, 5, 100, 0, 0, 594, 595, 5, 97, 0, 0, 595, 596, 5, 121, 0, 0, 596, 603, 5, 115, 0, 0, 597, 598, 5, 119, 0, 0, 598, 599, 5, 101, 0, 0, 599, 600, 5, 101, 0, 0, 600, 601, 5, 107, 0, 0, 601, 603, 5, 115, 0, 0, 602, 545, 1, 0, 0, 0, 602, 553, 1, 0, 0, 0, 602, 557, 1, 0, 0, 0, 602, 563, 1, 0, 0, 0, 602, 567, 1, 0, 0, 0, 602, 574, 1, 0, 0, 0, 602, 581, 1, 0, 0, 0, 602, 588, 1, 0, 0, 0, 602, 593, 1, 0, 0, 0, 602, 597, 1, 0, 0, 0, 603, 122, 1, 0, 0, 0, 604, 606, 5, 45, 0, 0, 605, 604, 1, 0, 0, 0, 605, 606, 1, 0, 0, 0, 606, 607, 1, 0, 0, 0, 607, 609, 3, 125, 62, 0, 608, 610, 3, 127, 63, 0, 609, 608, 1, 0, 0, 0, 609, 610, 1, 0, 0, 0, 610, 124, 1, 0, 0, 0, 611, 613, 7, 0, 0, 0, 612, 611, 1, 0, 0, 0, 613, 614, 1, 0, 0, 0, 614, 612, 1, 0, 0, 0, 614, 615, 1, 0, 0, 0, 615, 624, 1, 0, 0, 0, 616, 618, 5, 95, 0, 0, 617, 619, 7, 0, 0, 0, 618, 617, 1, 0, 0, 0, 619, 620, 1, 0, 0, 0, 620, 618, 1, 0, 0, 0, 620, 621, 1, 0, 0, 0, 621, 623, 1, 0, 0, 0, 622, 616, 1, 0, 0, 0, 623, 626, 1, 0, 0, 0, 624, 622, 1, 0, 0, 0, 624, 625, 1, 0, 0, 0, 625, 126, 1, 0, 0, 0, 626, 624, 1, 0, 0, 0, 627, 628, 7, 1, 0, 0, 628, 629, 3, 125, 62, 0, 629, 128, 1, 0, 0, 0, 630, 631, 5, 105, 0, 0, 631, 632, 5, 110, 0, 0, 632, 660, 5, 116, 0, 0, 633, 634, 5, 98, 0, 0, 634, 635, 5, 111, 0, 0, 635, 636, 5, 111, 0, 0, 636, 660, 5, 108, 0, 0, 637, 638, 5, 115, 0, 0, 638, 639, 5, 116, 0, 0, 639, 640, 5, 114, 0, 0, 640, 641, 5, 105, 0, 0, 641, 642, 5, 110, 0, 0, 642, 660, 5, 103, 0, 0, 643, 644, 5, 112, 0, 0, 644, 645, 5, 117, 0, 0, 645, 646, 5, 98, 0, 0, 646, 647, 5, 107, 0, 0, 647, 648, 5, 101, 0, 0, 648, 660, 5, 121, 0, 0, 649, 650, 5, 115, 0, 0, 650, 651, 5, 105, 0, 0, 651, 660, 5, 103, 0, 0, 652, 653, 5, 100, 0, 0, 653, 654, 5, 97, 0, 0, 654, 655, 5, 116, 0, 0, 655, 656, 5, 97, 0, 0, 656, 657, 5, 115, 0, 0, 657, 658, 5, 105, 0, 0, 658, 660, 5, 103, 0, 0, 659, 630, 1, 0, 0, 0, 659, 633, 1, 0, 0, 0, 659, 637, 1, 0, 0, 0, 659, 643, 1, 0, 0, 0, 659, 649, 1, 0, 0, 0, 659, 652, 1, 0, 0, 0, 660, 130, 1, 0, 0, 0, 661, 662, 5, 98, 0, 0, 662, 663, 5, 121, 0, 0, 663, 664, 5, 116, 0, 0, 664, 665, 5, 101, 0, 0, 665, 666, 5, 115, 0, 0, 666, 132, 1, 0, 0, 0, 667, 668, 5, 98, 0, 0, 668, 669, 5, 121, 0, 0, 669, 670, 5, 116, 0, 0, 670, 671, 5, 101, 0, 0, 671, 672, 5, 115, 0, 0, 672, 673, 1, 0, 0, 0, 673, 679, 3, 135, 67, 0, 674, 675, 5, 98, 0, 0, 675, 676, 5, 121, 0, 0, 676, 677, 5, 116, 0, 0, 677, 679, 5, 101, 0, 0, 678, 667, 1, 0, 0, 0, 678, 674, 1, 0, 0, 0, 679, 134, 1, 0, 0, 0, 680, 684, 7, 2, 0, 0, 681, 683, 7, 0, 0, 0, 682, 681, 1, 0, 0, 0, 683, 686, 1, 0, 0, 0, 684, 682, 1, 0, 0, 0, 684, 685, 1, 0, 0, 0, 685, 136, 1, 0, 0, 0, 686, 684, 1, 0, 0, 0, 687, 693, 5, 34, 0, 0, 688, 689, 5, 92, 0, 0, 689, 692, 5, 34, 0, 0, 690, 692, 8, 3, 0, 0, 691, 688, 1, 0, 0, 0, 691, 690, 1, 0, 0, 0, 692, 695, 1, 0, 0, 0, 693, 694, 1, 0, 0, 0, 693, 691, 1, 0, 0, 0, 694, 696, 1, 0, 0, 0, 695, 693, 1, 0, 0, 0, 696, 708, 5, 34, 0, 0, 697, 703, 5, 39, 0, 0, 698, 699, 5, 92, 0, 0, 699, 702, 5, 39, 0, 0, 700, 702, 8, 4, 0, 0, 701, 698, 1, 0, 0, 0, 701, 700, 1, 0, 0, 0, 702, 705, 1, 0, 0, 0, 703, 704, 1, 0, 0, 0, 703, 701, 1, 0, 0, 0, 704, 706, 1, 0, 0, 0, 705, 703, 1, 0, 0, 0, 706, 708, 5, 39, 0, 0, 707, 687, 1, 0, 0, 0, 707, 697, 1, 0, 0, 0, 708, 138, 1, 0, 0, 0, 709, 710, 5, 100, 0, 0, 710, 711, 5, 97, 0, 0, 711, 712, 5, 116, 0, 0, 712, 713, 5, 101, 0, 0, 713, 714, 5, 40, 0, 0, 714, 715, 1, 0, 0, 0, 715, 716, 3, 137, 68, 0, 716, 717, 5, 41, 0, 0, 717, 140, 1, 0, 0, 0, 718, 719, 5, 48, 0, 0, 719, 723, 7, 5, 0, 0, 720, 722, 7, 6, 0, 0, 721, 720, 1, 0, 0, 0, 722, 725, 1, 0, 0, 0, 723, 721, 1, 0, 0, 0, 723, 724, 1, 0, 0, 0, 724, 142, 1, 0, 0, 0, 725, 723, 1, 0, 0, 0, 726, 727, 5, 116, 0, 0, 727, 728, 5, 104, 0, 0, 728, 729, 5, 105, 0, 0, 729, 730, 5, 115, 0, 0, 730, 731, 5, 46, 0, 0, 731, 732, 5, 97, 0, 0, 732, 733, 5, 103, 0, 0, 733, 742, 5, 101, 0, 0, 734, 735, 5, 116, 0, 0, 735, 736, 5, 120, 0, 0, 736, 737, 5, 46, 0, 0, 737, 738, 5, 116, 0, 0, 738, 739, 5, 105, 0, 0, 739, 740, 5, 109, 0, 0, 740, 742, 5, 101, 0, 0, 741, 726, 1, 0, 0, 0, 741, 734, 1, 0, 0, 0, 742, 144, 1, 0, 0, 0, 743, 744, 5, 117, 0, 0, 744, 745, 5, 110, 0, 0, 745, 746, 5, 115, 0, 0, 746, 747, 5, 97, 0, 0, 747, 748, 5, 102, 0, 0, 748, 749, 5, 101, 0, 0, 749, 750, 5, 95, 0, 0, 750, 751, 5, 105, 0, 0, 751, 752, 5, 110, 0, 0, 752, 792, 5, 116, 0, 0, 753, 754, 5, 117, 0, 0, 754, 755, 5, 110, 0, 0, 755, 756, 5, 115, 0, 0, 756, 757, 5, 97, 0, 0, 757, 758, 5, 102, 0, 0, 758, 759, 5, 101, 0, 0, 759, 760, 5, 95, 0, 0, 760, 761, 5, 98, 0, 0, 761, 762, 5, 111, 0, 0, 762, 763, 5, 111, 0, 0, 763, 792, 5, 108, 0, 0, 764, 765, 5, 117, 0, 0, 765, 766, 5, 110, 0, 0, 766, 767, 5, 115, 0, 0, 767, 768, 5, 97, 0, 0, 768, 769, 5, 102, 0, 0, 769, 770, 5, 101, 0, 0, 770, 771, 5, 95, 0, 0, 771, 772, 5, 98, 0, 0, 772, 773, 5, 121, 0, 0, 773, 774, 5, 116, 0, 0, 774, 775, 5, 101, 0, 0, 775, 776, 5, 115, 0, 0, 776, 778, 1, 0, 0, 0, 777, 779, 3, 135, 67, 0, 778, 777, 1, 0, 0, 0, 778, 779, 1, 0, 0, 0, 779, 792, 1, 0, 0, 0, 780, 781, 5, 117, 0, 0, 781, 782, 5, 110, 0, 0, 782, 783, 5, 115, 0, 0, 783, 784, 5, 97, 0, 0, 784, 785, 5, 102, 0, 0, 785, 786, 5, 101, 0, 0, 786, 787, 5, 95, 0, 0, 787, 788, 5, 98, 0, 0, 788, 789, 5, 121, 0, 0, 789, 790, 5, 116, 0, 0, 790, 792, 5, 101, 0, 0, 791, 743, 1, 0, 0, 0, 791, 753, 1, 0, 0, 0, 791, 764, 1, 0, 0, 0, 791, 780, 1, 0, 0, 0, 792, 146, 1, 0, 0, 0, 793, 794, 5, 116, 0, 0, 794, 795, 5, 104, 0, 0, 795, 796, 5, 105, 0, 0, 796, 797, 5, 115, 0, 0, 797, 798, 5, 46, 0, 0, 798, 799, 5, 97, 0, 0, 799, 800, 5, 99, 0, 0, 800, 801, 5, 116, 0, 0, 801, 802, 5, 105, 0, 0, 802, 803, 5, 118, 0, 0, 803, 804, 5, 101, 0, 0, 804, 805, 5, 73, 0, 0, 805, 806, 5, 110, 0, 0, 806, 807, 5, 112, 0, 0, 807, 808, 5, 117, 0, 0, 808, 809, 5, 116, 0, 0, 809, 810, 5, 73, 0, 0, 810, 811, 5, 110, 0, 0, 811, 812, 5, 100, 0, 0, 812, 813, 5, 101, 0, 0, 813, 888, 5, 120, 0, 0, 814, 815, 5, 116, 0, 0, 815, 816, 5, 104, 0, 0, 816, 817, 5, 105, 0, 0, 817, 818, 5, 115, 0, 0, 818, 819, 5, 46, 0, 0, 819, 820, 5, 97, 0, 0, 820, 821, 5, 99, 0, 0, 821, 822, 5, 116, 0, 0, 822, 823, 5, 105, 0, 0, 823, 824, 5, 118, 0, 0, 824, 825, 5, 101, 0, 0, 825, 826, 5, 66, 0, 0, 826, 827, 5, 121, 0, 0, 827, 828, 5, 116, 0, 0, 828, 829, 5, 101, 0, 0, 829, 830, 5, 99, 0, 0, 830, 831, 5, 111, 0, 0, 831, 832, 5, 100, 0, 0, 832, 888, 5, 101, 0, 0, 833, 834, 5, 116, 0, 0, 834, 835, 5, 120, 0, 0, 835, 836, 5, 46, 0, 0, 836, 837, 5, 105, 0, 0, 837, 838, 5, 110, 0, 0, 838, 839, 5, 112, 0, 0, 839, 840, 5, 117, 0, 0, 840, 841, 5, 116, 0, 0, 841, 842, 5, 115, 0, 0, 842, 843, 5, 46, 0, 0, 843, 844, 5, 108, 0, 0, 844, 845, 5, 101, 0, 0, 845, 846, 5, 110, 0, 0, 846, 847, 5, 103, 0, 0, 847, 848, 5, 116, 0, 0, 848, 888, 5, 104, 0, 0, 849, 850, 5, 116, 0, 0, 850, 851, 5, 120, 0, 0, 851, 852, 5, 46, 0, 0, 852, 853, 5, 111, 0, 0, 853, 854, 5, 117, 0, 0, 854, 855, 5, 116, 0, 0, 855, 856, 5, 112, 0, 0, 856, 857, 5, 117, 0, 0, 857, 858, 5, 116, 0, 0, 858, 859, 5, 115, 0, 0, 859, 860, 5, 46, 0, 0, 860, 861, 5, 108, 0, 0, 861, 862, 5, 101, 0, 0, 862, 863, 5, 110, 0, 0, 863, 864, 5, 103, 0, 0, 864, 865, 5, 116, 0, 0, 865, 888, 5, 104, 0, 0, 866, 867, 5, 116, 0, 0, 867, 868, 5, 120, 0, 0, 868, 869, 5, 46, 0, 0, 869, 870, 5, 118, 0, 0, 870, 871, 5, 101, 0, 0, 871, 872, 5, 114, 0, 0, 872, 873, 5, 115, 0, 0, 873, 874, 5, 105, 0, 0, 874, 875, 5, 111, 0, 0, 875, 888, 5, 110, 0, 0, 876, 877, 5, 116, 0, 0, 877, 878, 5, 120, 0, 0, 878, 879, 5, 46, 0, 0, 879, 880, 5, 108, 0, 0, 880, 881, 5, 111, 0, 0, 881, 882, 5, 99, 0, 0, 882, 883, 5, 107, 0, 0, 883, 884, 5, 116, 0, 0, 884, 885, 5, 105, 0, 0, 885, 886, 5, 109, 0, 0, 886, 888, 5, 101, 0, 0, 887, 793, 1, 0, 0, 0, 887, 814, 1, 0, 0, 0, 887, 833, 1, 0, 0, 0, 887, 849, 1, 0, 0, 0, 887, 866, 1, 0, 0, 0, 887, 876, 1, 0, 0, 0, 888, 148, 1, 0, 0, 0, 889, 893, 7, 7, 0, 0, 890, 892, 7, 8, 0, 0, 891, 890, 1, 0, 0, 0, 892, 895, 1, 0, 0, 0, 893, 891, 1, 0, 0, 0, 893, 894, 1, 0, 0, 0, 894, 150, 1, 0, 0, 0, 895, 893, 1, 0, 0, 0, 896, 898, 7, 9, 0, 0, 897, 896, 1, 0, 0, 0, 898, 899, 1, 0, 0, 0, 899, 897, 1, 0, 0, 0, 899, 900, 1, 0, 0, 0, 900, 901, 1, 0, 0, 0, 901, 902, 6, 75, 0, 0, 902, 152, 1, 0, 0, 0, 903, 904, 5, 47, 0, 0, 904, 905, 5, 42, 0, 0, 905, 909, 1, 0, 0, 0, 906, 908, 9, 0, 0, 0, 907, 906, 1, 0, 0, 0, 908, 911, 1, 0, 0, 0, 909, 910, 1, 0, 0, 0, 909, 907, 1, 0, 0, 0, 910, 912, 1, 0, 0, 0, 911, 909, 1, 0, 0, 0, 912, 913, 5, 42, 0, 0, 913, 914, 5, 47, 0, 0, 914, 915, 1, 0, 0, 0, 915, 916, 6, 76, 1, 0, 916, 154, 1, 0, 0, 0, 917, 918, 5, 47, 0, 0, 918, 919, 5, 47, 0, 0, 919, 923, 1, 0, 0, 0, 920, 922, 8, 10, 0, 0, 921, 920, 1, 0, 0, 0, 922, 925, 1, 0, 0, 0, 923, 921, 1, 0, 0, 0, 923, 924, 1, 0, 0, 0, 924, 926, 1, 0, 0, 0, 925, 923, 1, 0, 0, 0, 926, 927, 6, 77, 1, 0, 927, 156, 1, 0, 0, 0, 28, 0, 520, 526, 532, 543, 602, 605, 609, 614, 620, 624, 659, 678, 684, 691, 693, 701, 703, 707, 723, 741, 778, 791, 887, 893, 899, 909, 923, 2, 6, 0, 0, 0, 1, 0] \ No newline at end of file diff --git a/packages/cashc/src/grammar/CashScriptLexer.tokens b/packages/cashc/src/grammar/CashScriptLexer.tokens index 3b21c6f2..216b113a 100644 --- a/packages/cashc/src/grammar/CashScriptLexer.tokens +++ b/packages/cashc/src/grammar/CashScriptLexer.tokens @@ -55,26 +55,27 @@ T__53=54 T__54=55 T__55=56 T__56=57 -VersionLiteral=58 -BooleanLiteral=59 -NumberUnit=60 -NumberLiteral=61 -NumberPart=62 -ExponentPart=63 -PrimitiveType=64 -UnboundedBytes=65 -BoundedBytes=66 -Bound=67 -StringLiteral=68 -DateLiteral=69 -HexLiteral=70 -TxVar=71 -UnsafeCast=72 -NullaryOp=73 -Identifier=74 -WHITESPACE=75 -COMMENT=76 -LINE_COMMENT=77 +T__57=58 +VersionLiteral=59 +BooleanLiteral=60 +NumberUnit=61 +NumberLiteral=62 +NumberPart=63 +ExponentPart=64 +PrimitiveType=65 +UnboundedBytes=66 +BoundedBytes=67 +Bound=68 +StringLiteral=69 +DateLiteral=70 +HexLiteral=71 +TxVar=72 +UnsafeCast=73 +NullaryOp=74 +Identifier=75 +WHITESPACE=76 +COMMENT=77 +LINE_COMMENT=78 'pragma'=1 ';'=2 'cashscript'=3 @@ -88,48 +89,49 @@ LINE_COMMENT=77 'contract'=11 '{'=12 '}'=13 -'function'=14 -'('=15 -','=16 -')'=17 -'require'=18 -'console.log'=19 -'if'=20 -'else'=21 -'do'=22 -'while'=23 -'for'=24 -'new'=25 -'['=26 -']'=27 -'tx.outputs'=28 -'.value'=29 -'.lockingBytecode'=30 -'.tokenCategory'=31 -'.nftCommitment'=32 -'.tokenAmount'=33 -'tx.inputs'=34 -'.outpointTransactionHash'=35 -'.outpointIndex'=36 -'.unlockingBytecode'=37 -'.sequenceNumber'=38 -'.reverse()'=39 -'.length'=40 -'.split'=41 -'.slice'=42 -'!'=43 -'-'=44 -'*'=45 -'/'=46 -'%'=47 -'+'=48 -'>>'=49 -'<<'=50 -'=='=51 -'!='=52 -'&'=53 -'|'=54 -'&&'=55 -'||'=56 -'constant'=57 -'bytes'=65 +'library'=14 +'function'=15 +'('=16 +','=17 +')'=18 +'require'=19 +'console.log'=20 +'if'=21 +'else'=22 +'do'=23 +'while'=24 +'for'=25 +'new'=26 +'['=27 +']'=28 +'tx.outputs'=29 +'.value'=30 +'.lockingBytecode'=31 +'.tokenCategory'=32 +'.nftCommitment'=33 +'.tokenAmount'=34 +'tx.inputs'=35 +'.outpointTransactionHash'=36 +'.outpointIndex'=37 +'.unlockingBytecode'=38 +'.sequenceNumber'=39 +'.reverse()'=40 +'.length'=41 +'.split'=42 +'.slice'=43 +'!'=44 +'-'=45 +'*'=46 +'/'=47 +'%'=48 +'+'=49 +'>>'=50 +'<<'=51 +'=='=52 +'!='=53 +'&'=54 +'|'=55 +'&&'=56 +'||'=57 +'constant'=58 +'bytes'=66 diff --git a/packages/cashc/src/grammar/CashScriptLexer.ts b/packages/cashc/src/grammar/CashScriptLexer.ts index 3f8913a6..8df549e0 100644 --- a/packages/cashc/src/grammar/CashScriptLexer.ts +++ b/packages/cashc/src/grammar/CashScriptLexer.ts @@ -1,4 +1,4 @@ -// Generated from src/grammar/CashScript.g4 by ANTLR 4.13.1 +// Generated from src/grammar/CashScript.g4 by ANTLR 4.13.2 // noinspection ES6UnusedImports,JSUnusedGlobalSymbols,JSUnusedLocalSymbols import { ATN, @@ -69,26 +69,27 @@ export default class CashScriptLexer extends Lexer { public static readonly T__54 = 55; public static readonly T__55 = 56; public static readonly T__56 = 57; - public static readonly VersionLiteral = 58; - public static readonly BooleanLiteral = 59; - public static readonly NumberUnit = 60; - public static readonly NumberLiteral = 61; - public static readonly NumberPart = 62; - public static readonly ExponentPart = 63; - public static readonly PrimitiveType = 64; - public static readonly UnboundedBytes = 65; - public static readonly BoundedBytes = 66; - public static readonly Bound = 67; - public static readonly StringLiteral = 68; - public static readonly DateLiteral = 69; - public static readonly HexLiteral = 70; - public static readonly TxVar = 71; - public static readonly UnsafeCast = 72; - public static readonly NullaryOp = 73; - public static readonly Identifier = 74; - public static readonly WHITESPACE = 75; - public static readonly COMMENT = 76; - public static readonly LINE_COMMENT = 77; + public static readonly T__57 = 58; + public static readonly VersionLiteral = 59; + public static readonly BooleanLiteral = 60; + public static readonly NumberUnit = 61; + public static readonly NumberLiteral = 62; + public static readonly NumberPart = 63; + public static readonly ExponentPart = 64; + public static readonly PrimitiveType = 65; + public static readonly UnboundedBytes = 66; + public static readonly BoundedBytes = 67; + public static readonly Bound = 68; + public static readonly StringLiteral = 69; + public static readonly DateLiteral = 70; + public static readonly HexLiteral = 71; + public static readonly TxVar = 72; + public static readonly UnsafeCast = 73; + public static readonly NullaryOp = 74; + public static readonly Identifier = 75; + public static readonly WHITESPACE = 76; + public static readonly COMMENT = 77; + public static readonly LINE_COMMENT = 78; public static readonly EOF = Token.EOF; public static readonly channelNames: string[] = [ "DEFAULT_TOKEN_CHANNEL", "HIDDEN" ]; @@ -99,6 +100,7 @@ export default class CashScriptLexer extends Lexer { "'<'", "'<='", "'='", "'contract'", "'{'", "'}'", + "'library'", "'function'", "'('", "','", "')'", "'require'", @@ -163,7 +165,7 @@ export default class CashScriptLexer extends Lexer { null, null, null, null, null, null, - "VersionLiteral", + null, "VersionLiteral", "BooleanLiteral", "NumberUnit", "NumberLiteral", @@ -191,10 +193,10 @@ export default class CashScriptLexer extends Lexer { "T__33", "T__34", "T__35", "T__36", "T__37", "T__38", "T__39", "T__40", "T__41", "T__42", "T__43", "T__44", "T__45", "T__46", "T__47", "T__48", "T__49", "T__50", "T__51", "T__52", "T__53", "T__54", "T__55", "T__56", - "VersionLiteral", "BooleanLiteral", "NumberUnit", "NumberLiteral", "NumberPart", - "ExponentPart", "PrimitiveType", "UnboundedBytes", "BoundedBytes", "Bound", - "StringLiteral", "DateLiteral", "HexLiteral", "TxVar", "UnsafeCast", "NullaryOp", - "Identifier", "WHITESPACE", "COMMENT", "LINE_COMMENT", + "T__57", "VersionLiteral", "BooleanLiteral", "NumberUnit", "NumberLiteral", + "NumberPart", "ExponentPart", "PrimitiveType", "UnboundedBytes", "BoundedBytes", + "Bound", "StringLiteral", "DateLiteral", "HexLiteral", "TxVar", "UnsafeCast", + "NullaryOp", "Identifier", "WHITESPACE", "COMMENT", "LINE_COMMENT", ]; @@ -215,7 +217,7 @@ export default class CashScriptLexer extends Lexer { public get modeNames(): string[] { return CashScriptLexer.modeNames; } - public static readonly _serializedATN: number[] = [4,0,77,918,6,-1,2,0, + public static readonly _serializedATN: number[] = [4,0,78,928,6,-1,2,0, 7,0,2,1,7,1,2,2,7,2,2,3,7,3,2,4,7,4,2,5,7,5,2,6,7,6,2,7,7,7,2,8,7,8,2,9, 7,9,2,10,7,10,2,11,7,11,2,12,7,12,2,13,7,13,2,14,7,14,2,15,7,15,2,16,7, 16,2,17,7,17,2,18,7,18,2,19,7,19,2,20,7,20,2,21,7,21,2,22,7,22,2,23,7,23, @@ -226,297 +228,300 @@ export default class CashScriptLexer extends Lexer { 2,53,7,53,2,54,7,54,2,55,7,55,2,56,7,56,2,57,7,57,2,58,7,58,2,59,7,59,2, 60,7,60,2,61,7,61,2,62,7,62,2,63,7,63,2,64,7,64,2,65,7,65,2,66,7,66,2,67, 7,67,2,68,7,68,2,69,7,69,2,70,7,70,2,71,7,71,2,72,7,72,2,73,7,73,2,74,7, - 74,2,75,7,75,2,76,7,76,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1,1,1,1,2,1,2,1,2, - 1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,3,1,3,1,4,1,4,1,5,1,5,1,5,1,6,1,6,1,7, - 1,7,1,8,1,8,1,8,1,9,1,9,1,10,1,10,1,10,1,10,1,10,1,10,1,10,1,10,1,10,1, - 11,1,11,1,12,1,12,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,14,1,14, - 1,15,1,15,1,16,1,16,1,17,1,17,1,17,1,17,1,17,1,17,1,17,1,17,1,18,1,18,1, - 18,1,18,1,18,1,18,1,18,1,18,1,18,1,18,1,18,1,18,1,19,1,19,1,19,1,20,1,20, - 1,20,1,20,1,20,1,21,1,21,1,21,1,22,1,22,1,22,1,22,1,22,1,22,1,23,1,23,1, - 23,1,23,1,24,1,24,1,24,1,24,1,25,1,25,1,26,1,26,1,27,1,27,1,27,1,27,1,27, - 1,27,1,27,1,27,1,27,1,27,1,27,1,28,1,28,1,28,1,28,1,28,1,28,1,28,1,29,1, - 29,1,29,1,29,1,29,1,29,1,29,1,29,1,29,1,29,1,29,1,29,1,29,1,29,1,29,1,29, - 1,29,1,30,1,30,1,30,1,30,1,30,1,30,1,30,1,30,1,30,1,30,1,30,1,30,1,30,1, - 30,1,30,1,31,1,31,1,31,1,31,1,31,1,31,1,31,1,31,1,31,1,31,1,31,1,31,1,31, - 1,31,1,31,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1, - 32,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,34,1,34,1,34,1,34, - 1,34,1,34,1,34,1,34,1,34,1,34,1,34,1,34,1,34,1,34,1,34,1,34,1,34,1,34,1, - 34,1,34,1,34,1,34,1,34,1,34,1,34,1,35,1,35,1,35,1,35,1,35,1,35,1,35,1,35, - 1,35,1,35,1,35,1,35,1,35,1,35,1,35,1,36,1,36,1,36,1,36,1,36,1,36,1,36,1, - 36,1,36,1,36,1,36,1,36,1,36,1,36,1,36,1,36,1,36,1,36,1,36,1,37,1,37,1,37, - 1,37,1,37,1,37,1,37,1,37,1,37,1,37,1,37,1,37,1,37,1,37,1,37,1,37,1,38,1, - 38,1,38,1,38,1,38,1,38,1,38,1,38,1,38,1,38,1,38,1,39,1,39,1,39,1,39,1,39, - 1,39,1,39,1,39,1,40,1,40,1,40,1,40,1,40,1,40,1,40,1,41,1,41,1,41,1,41,1, - 41,1,41,1,41,1,42,1,42,1,43,1,43,1,44,1,44,1,45,1,45,1,46,1,46,1,47,1,47, - 1,48,1,48,1,48,1,49,1,49,1,49,1,50,1,50,1,50,1,51,1,51,1,51,1,52,1,52,1, - 53,1,53,1,54,1,54,1,54,1,55,1,55,1,55,1,56,1,56,1,56,1,56,1,56,1,56,1,56, - 1,56,1,56,1,57,4,57,509,8,57,11,57,12,57,510,1,57,1,57,4,57,515,8,57,11, - 57,12,57,516,1,57,1,57,4,57,521,8,57,11,57,12,57,522,1,58,1,58,1,58,1,58, - 1,58,1,58,1,58,1,58,1,58,3,58,534,8,58,1,59,1,59,1,59,1,59,1,59,1,59,1, - 59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59, - 1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1, - 59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59, - 1,59,1,59,1,59,1,59,1,59,1,59,1,59,3,59,593,8,59,1,60,3,60,596,8,60,1,60, - 1,60,3,60,600,8,60,1,61,4,61,603,8,61,11,61,12,61,604,1,61,1,61,4,61,609, - 8,61,11,61,12,61,610,5,61,613,8,61,10,61,12,61,616,9,61,1,62,1,62,1,62, - 1,63,1,63,1,63,1,63,1,63,1,63,1,63,1,63,1,63,1,63,1,63,1,63,1,63,1,63,1, - 63,1,63,1,63,1,63,1,63,1,63,1,63,1,63,1,63,1,63,1,63,1,63,1,63,1,63,1,63, - 3,63,650,8,63,1,64,1,64,1,64,1,64,1,64,1,64,1,65,1,65,1,65,1,65,1,65,1, - 65,1,65,1,65,1,65,1,65,1,65,3,65,669,8,65,1,66,1,66,5,66,673,8,66,10,66, - 12,66,676,9,66,1,67,1,67,1,67,1,67,5,67,682,8,67,10,67,12,67,685,9,67,1, - 67,1,67,1,67,1,67,1,67,5,67,692,8,67,10,67,12,67,695,9,67,1,67,3,67,698, - 8,67,1,68,1,68,1,68,1,68,1,68,1,68,1,68,1,68,1,68,1,69,1,69,1,69,5,69,712, - 8,69,10,69,12,69,715,9,69,1,70,1,70,1,70,1,70,1,70,1,70,1,70,1,70,1,70, - 1,70,1,70,1,70,1,70,1,70,1,70,3,70,732,8,70,1,71,1,71,1,71,1,71,1,71,1, - 71,1,71,1,71,1,71,1,71,1,71,1,71,1,71,1,71,1,71,1,71,1,71,1,71,1,71,1,71, - 1,71,1,71,1,71,1,71,1,71,1,71,1,71,1,71,1,71,1,71,1,71,1,71,1,71,1,71,1, - 71,3,71,769,8,71,1,71,1,71,1,71,1,71,1,71,1,71,1,71,1,71,1,71,1,71,1,71, - 3,71,782,8,71,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1, - 72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72, + 74,2,75,7,75,2,76,7,76,2,77,7,77,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1,1,1,1, + 2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,3,1,3,1,4,1,4,1,5,1,5,1,5,1, + 6,1,6,1,7,1,7,1,8,1,8,1,8,1,9,1,9,1,10,1,10,1,10,1,10,1,10,1,10,1,10,1, + 10,1,10,1,11,1,11,1,12,1,12,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,14, + 1,14,1,14,1,14,1,14,1,14,1,14,1,14,1,14,1,15,1,15,1,16,1,16,1,17,1,17,1, + 18,1,18,1,18,1,18,1,18,1,18,1,18,1,18,1,19,1,19,1,19,1,19,1,19,1,19,1,19, + 1,19,1,19,1,19,1,19,1,19,1,20,1,20,1,20,1,21,1,21,1,21,1,21,1,21,1,22,1, + 22,1,22,1,23,1,23,1,23,1,23,1,23,1,23,1,24,1,24,1,24,1,24,1,25,1,25,1,25, + 1,25,1,26,1,26,1,27,1,27,1,28,1,28,1,28,1,28,1,28,1,28,1,28,1,28,1,28,1, + 28,1,28,1,29,1,29,1,29,1,29,1,29,1,29,1,29,1,30,1,30,1,30,1,30,1,30,1,30, + 1,30,1,30,1,30,1,30,1,30,1,30,1,30,1,30,1,30,1,30,1,30,1,31,1,31,1,31,1, + 31,1,31,1,31,1,31,1,31,1,31,1,31,1,31,1,31,1,31,1,31,1,31,1,32,1,32,1,32, + 1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,33,1,33,1, + 33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,34,1,34,1,34,1,34, + 1,34,1,34,1,34,1,34,1,34,1,34,1,35,1,35,1,35,1,35,1,35,1,35,1,35,1,35,1, + 35,1,35,1,35,1,35,1,35,1,35,1,35,1,35,1,35,1,35,1,35,1,35,1,35,1,35,1,35, + 1,35,1,35,1,36,1,36,1,36,1,36,1,36,1,36,1,36,1,36,1,36,1,36,1,36,1,36,1, + 36,1,36,1,36,1,37,1,37,1,37,1,37,1,37,1,37,1,37,1,37,1,37,1,37,1,37,1,37, + 1,37,1,37,1,37,1,37,1,37,1,37,1,37,1,38,1,38,1,38,1,38,1,38,1,38,1,38,1, + 38,1,38,1,38,1,38,1,38,1,38,1,38,1,38,1,38,1,39,1,39,1,39,1,39,1,39,1,39, + 1,39,1,39,1,39,1,39,1,39,1,40,1,40,1,40,1,40,1,40,1,40,1,40,1,40,1,41,1, + 41,1,41,1,41,1,41,1,41,1,41,1,42,1,42,1,42,1,42,1,42,1,42,1,42,1,43,1,43, + 1,44,1,44,1,45,1,45,1,46,1,46,1,47,1,47,1,48,1,48,1,49,1,49,1,49,1,50,1, + 50,1,50,1,51,1,51,1,51,1,52,1,52,1,52,1,53,1,53,1,54,1,54,1,55,1,55,1,55, + 1,56,1,56,1,56,1,57,1,57,1,57,1,57,1,57,1,57,1,57,1,57,1,57,1,58,4,58,519, + 8,58,11,58,12,58,520,1,58,1,58,4,58,525,8,58,11,58,12,58,526,1,58,1,58, + 4,58,531,8,58,11,58,12,58,532,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1, + 59,3,59,544,8,59,1,60,1,60,1,60,1,60,1,60,1,60,1,60,1,60,1,60,1,60,1,60, + 1,60,1,60,1,60,1,60,1,60,1,60,1,60,1,60,1,60,1,60,1,60,1,60,1,60,1,60,1, + 60,1,60,1,60,1,60,1,60,1,60,1,60,1,60,1,60,1,60,1,60,1,60,1,60,1,60,1,60, + 1,60,1,60,1,60,1,60,1,60,1,60,1,60,1,60,1,60,1,60,1,60,1,60,1,60,1,60,1, + 60,1,60,1,60,3,60,603,8,60,1,61,3,61,606,8,61,1,61,1,61,3,61,610,8,61,1, + 62,4,62,613,8,62,11,62,12,62,614,1,62,1,62,4,62,619,8,62,11,62,12,62,620, + 5,62,623,8,62,10,62,12,62,626,9,62,1,63,1,63,1,63,1,64,1,64,1,64,1,64,1, + 64,1,64,1,64,1,64,1,64,1,64,1,64,1,64,1,64,1,64,1,64,1,64,1,64,1,64,1,64, + 1,64,1,64,1,64,1,64,1,64,1,64,1,64,1,64,1,64,1,64,3,64,660,8,64,1,65,1, + 65,1,65,1,65,1,65,1,65,1,66,1,66,1,66,1,66,1,66,1,66,1,66,1,66,1,66,1,66, + 1,66,3,66,679,8,66,1,67,1,67,5,67,683,8,67,10,67,12,67,686,9,67,1,68,1, + 68,1,68,1,68,5,68,692,8,68,10,68,12,68,695,9,68,1,68,1,68,1,68,1,68,1,68, + 5,68,702,8,68,10,68,12,68,705,9,68,1,68,3,68,708,8,68,1,69,1,69,1,69,1, + 69,1,69,1,69,1,69,1,69,1,69,1,70,1,70,1,70,5,70,722,8,70,10,70,12,70,725, + 9,70,1,71,1,71,1,71,1,71,1,71,1,71,1,71,1,71,1,71,1,71,1,71,1,71,1,71,1, + 71,1,71,3,71,742,8,71,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72, 1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1, - 72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72, - 1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1, - 72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72, - 1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,3,72,878,8,72,1,73,1, - 73,5,73,882,8,73,10,73,12,73,885,9,73,1,74,4,74,888,8,74,11,74,12,74,889, - 1,74,1,74,1,75,1,75,1,75,1,75,5,75,898,8,75,10,75,12,75,901,9,75,1,75,1, - 75,1,75,1,75,1,75,1,76,1,76,1,76,1,76,5,76,912,8,76,10,76,12,76,915,9,76, - 1,76,1,76,3,683,693,899,0,77,1,1,3,2,5,3,7,4,9,5,11,6,13,7,15,8,17,9,19, - 10,21,11,23,12,25,13,27,14,29,15,31,16,33,17,35,18,37,19,39,20,41,21,43, - 22,45,23,47,24,49,25,51,26,53,27,55,28,57,29,59,30,61,31,63,32,65,33,67, - 34,69,35,71,36,73,37,75,38,77,39,79,40,81,41,83,42,85,43,87,44,89,45,91, - 46,93,47,95,48,97,49,99,50,101,51,103,52,105,53,107,54,109,55,111,56,113, - 57,115,58,117,59,119,60,121,61,123,62,125,63,127,64,129,65,131,66,133,67, - 135,68,137,69,139,70,141,71,143,72,145,73,147,74,149,75,151,76,153,77,1, - 0,11,1,0,48,57,2,0,69,69,101,101,1,0,49,57,3,0,10,10,13,13,34,34,3,0,10, - 10,13,13,39,39,2,0,88,88,120,120,3,0,48,57,65,70,97,102,2,0,65,90,97,122, - 4,0,48,57,65,90,95,95,97,122,3,0,9,10,12,13,32,32,2,0,10,10,13,13,962,0, - 1,1,0,0,0,0,3,1,0,0,0,0,5,1,0,0,0,0,7,1,0,0,0,0,9,1,0,0,0,0,11,1,0,0,0, - 0,13,1,0,0,0,0,15,1,0,0,0,0,17,1,0,0,0,0,19,1,0,0,0,0,21,1,0,0,0,0,23,1, - 0,0,0,0,25,1,0,0,0,0,27,1,0,0,0,0,29,1,0,0,0,0,31,1,0,0,0,0,33,1,0,0,0, - 0,35,1,0,0,0,0,37,1,0,0,0,0,39,1,0,0,0,0,41,1,0,0,0,0,43,1,0,0,0,0,45,1, - 0,0,0,0,47,1,0,0,0,0,49,1,0,0,0,0,51,1,0,0,0,0,53,1,0,0,0,0,55,1,0,0,0, - 0,57,1,0,0,0,0,59,1,0,0,0,0,61,1,0,0,0,0,63,1,0,0,0,0,65,1,0,0,0,0,67,1, - 0,0,0,0,69,1,0,0,0,0,71,1,0,0,0,0,73,1,0,0,0,0,75,1,0,0,0,0,77,1,0,0,0, - 0,79,1,0,0,0,0,81,1,0,0,0,0,83,1,0,0,0,0,85,1,0,0,0,0,87,1,0,0,0,0,89,1, - 0,0,0,0,91,1,0,0,0,0,93,1,0,0,0,0,95,1,0,0,0,0,97,1,0,0,0,0,99,1,0,0,0, - 0,101,1,0,0,0,0,103,1,0,0,0,0,105,1,0,0,0,0,107,1,0,0,0,0,109,1,0,0,0,0, - 111,1,0,0,0,0,113,1,0,0,0,0,115,1,0,0,0,0,117,1,0,0,0,0,119,1,0,0,0,0,121, - 1,0,0,0,0,123,1,0,0,0,0,125,1,0,0,0,0,127,1,0,0,0,0,129,1,0,0,0,0,131,1, - 0,0,0,0,133,1,0,0,0,0,135,1,0,0,0,0,137,1,0,0,0,0,139,1,0,0,0,0,141,1,0, - 0,0,0,143,1,0,0,0,0,145,1,0,0,0,0,147,1,0,0,0,0,149,1,0,0,0,0,151,1,0,0, - 0,0,153,1,0,0,0,1,155,1,0,0,0,3,162,1,0,0,0,5,164,1,0,0,0,7,175,1,0,0,0, - 9,177,1,0,0,0,11,179,1,0,0,0,13,182,1,0,0,0,15,184,1,0,0,0,17,186,1,0,0, - 0,19,189,1,0,0,0,21,191,1,0,0,0,23,200,1,0,0,0,25,202,1,0,0,0,27,204,1, - 0,0,0,29,213,1,0,0,0,31,215,1,0,0,0,33,217,1,0,0,0,35,219,1,0,0,0,37,227, - 1,0,0,0,39,239,1,0,0,0,41,242,1,0,0,0,43,247,1,0,0,0,45,250,1,0,0,0,47, - 256,1,0,0,0,49,260,1,0,0,0,51,264,1,0,0,0,53,266,1,0,0,0,55,268,1,0,0,0, - 57,279,1,0,0,0,59,286,1,0,0,0,61,303,1,0,0,0,63,318,1,0,0,0,65,333,1,0, - 0,0,67,346,1,0,0,0,69,356,1,0,0,0,71,381,1,0,0,0,73,396,1,0,0,0,75,415, - 1,0,0,0,77,431,1,0,0,0,79,442,1,0,0,0,81,450,1,0,0,0,83,457,1,0,0,0,85, - 464,1,0,0,0,87,466,1,0,0,0,89,468,1,0,0,0,91,470,1,0,0,0,93,472,1,0,0,0, - 95,474,1,0,0,0,97,476,1,0,0,0,99,479,1,0,0,0,101,482,1,0,0,0,103,485,1, - 0,0,0,105,488,1,0,0,0,107,490,1,0,0,0,109,492,1,0,0,0,111,495,1,0,0,0,113, - 498,1,0,0,0,115,508,1,0,0,0,117,533,1,0,0,0,119,592,1,0,0,0,121,595,1,0, - 0,0,123,602,1,0,0,0,125,617,1,0,0,0,127,649,1,0,0,0,129,651,1,0,0,0,131, - 668,1,0,0,0,133,670,1,0,0,0,135,697,1,0,0,0,137,699,1,0,0,0,139,708,1,0, - 0,0,141,731,1,0,0,0,143,781,1,0,0,0,145,877,1,0,0,0,147,879,1,0,0,0,149, - 887,1,0,0,0,151,893,1,0,0,0,153,907,1,0,0,0,155,156,5,112,0,0,156,157,5, - 114,0,0,157,158,5,97,0,0,158,159,5,103,0,0,159,160,5,109,0,0,160,161,5, - 97,0,0,161,2,1,0,0,0,162,163,5,59,0,0,163,4,1,0,0,0,164,165,5,99,0,0,165, - 166,5,97,0,0,166,167,5,115,0,0,167,168,5,104,0,0,168,169,5,115,0,0,169, - 170,5,99,0,0,170,171,5,114,0,0,171,172,5,105,0,0,172,173,5,112,0,0,173, - 174,5,116,0,0,174,6,1,0,0,0,175,176,5,94,0,0,176,8,1,0,0,0,177,178,5,126, - 0,0,178,10,1,0,0,0,179,180,5,62,0,0,180,181,5,61,0,0,181,12,1,0,0,0,182, - 183,5,62,0,0,183,14,1,0,0,0,184,185,5,60,0,0,185,16,1,0,0,0,186,187,5,60, - 0,0,187,188,5,61,0,0,188,18,1,0,0,0,189,190,5,61,0,0,190,20,1,0,0,0,191, - 192,5,99,0,0,192,193,5,111,0,0,193,194,5,110,0,0,194,195,5,116,0,0,195, - 196,5,114,0,0,196,197,5,97,0,0,197,198,5,99,0,0,198,199,5,116,0,0,199,22, - 1,0,0,0,200,201,5,123,0,0,201,24,1,0,0,0,202,203,5,125,0,0,203,26,1,0,0, - 0,204,205,5,102,0,0,205,206,5,117,0,0,206,207,5,110,0,0,207,208,5,99,0, - 0,208,209,5,116,0,0,209,210,5,105,0,0,210,211,5,111,0,0,211,212,5,110,0, - 0,212,28,1,0,0,0,213,214,5,40,0,0,214,30,1,0,0,0,215,216,5,44,0,0,216,32, - 1,0,0,0,217,218,5,41,0,0,218,34,1,0,0,0,219,220,5,114,0,0,220,221,5,101, - 0,0,221,222,5,113,0,0,222,223,5,117,0,0,223,224,5,105,0,0,224,225,5,114, - 0,0,225,226,5,101,0,0,226,36,1,0,0,0,227,228,5,99,0,0,228,229,5,111,0,0, - 229,230,5,110,0,0,230,231,5,115,0,0,231,232,5,111,0,0,232,233,5,108,0,0, - 233,234,5,101,0,0,234,235,5,46,0,0,235,236,5,108,0,0,236,237,5,111,0,0, - 237,238,5,103,0,0,238,38,1,0,0,0,239,240,5,105,0,0,240,241,5,102,0,0,241, - 40,1,0,0,0,242,243,5,101,0,0,243,244,5,108,0,0,244,245,5,115,0,0,245,246, - 5,101,0,0,246,42,1,0,0,0,247,248,5,100,0,0,248,249,5,111,0,0,249,44,1,0, - 0,0,250,251,5,119,0,0,251,252,5,104,0,0,252,253,5,105,0,0,253,254,5,108, - 0,0,254,255,5,101,0,0,255,46,1,0,0,0,256,257,5,102,0,0,257,258,5,111,0, - 0,258,259,5,114,0,0,259,48,1,0,0,0,260,261,5,110,0,0,261,262,5,101,0,0, - 262,263,5,119,0,0,263,50,1,0,0,0,264,265,5,91,0,0,265,52,1,0,0,0,266,267, - 5,93,0,0,267,54,1,0,0,0,268,269,5,116,0,0,269,270,5,120,0,0,270,271,5,46, - 0,0,271,272,5,111,0,0,272,273,5,117,0,0,273,274,5,116,0,0,274,275,5,112, - 0,0,275,276,5,117,0,0,276,277,5,116,0,0,277,278,5,115,0,0,278,56,1,0,0, - 0,279,280,5,46,0,0,280,281,5,118,0,0,281,282,5,97,0,0,282,283,5,108,0,0, - 283,284,5,117,0,0,284,285,5,101,0,0,285,58,1,0,0,0,286,287,5,46,0,0,287, - 288,5,108,0,0,288,289,5,111,0,0,289,290,5,99,0,0,290,291,5,107,0,0,291, - 292,5,105,0,0,292,293,5,110,0,0,293,294,5,103,0,0,294,295,5,66,0,0,295, - 296,5,121,0,0,296,297,5,116,0,0,297,298,5,101,0,0,298,299,5,99,0,0,299, - 300,5,111,0,0,300,301,5,100,0,0,301,302,5,101,0,0,302,60,1,0,0,0,303,304, - 5,46,0,0,304,305,5,116,0,0,305,306,5,111,0,0,306,307,5,107,0,0,307,308, - 5,101,0,0,308,309,5,110,0,0,309,310,5,67,0,0,310,311,5,97,0,0,311,312,5, - 116,0,0,312,313,5,101,0,0,313,314,5,103,0,0,314,315,5,111,0,0,315,316,5, - 114,0,0,316,317,5,121,0,0,317,62,1,0,0,0,318,319,5,46,0,0,319,320,5,110, - 0,0,320,321,5,102,0,0,321,322,5,116,0,0,322,323,5,67,0,0,323,324,5,111, - 0,0,324,325,5,109,0,0,325,326,5,109,0,0,326,327,5,105,0,0,327,328,5,116, - 0,0,328,329,5,109,0,0,329,330,5,101,0,0,330,331,5,110,0,0,331,332,5,116, - 0,0,332,64,1,0,0,0,333,334,5,46,0,0,334,335,5,116,0,0,335,336,5,111,0,0, - 336,337,5,107,0,0,337,338,5,101,0,0,338,339,5,110,0,0,339,340,5,65,0,0, - 340,341,5,109,0,0,341,342,5,111,0,0,342,343,5,117,0,0,343,344,5,110,0,0, - 344,345,5,116,0,0,345,66,1,0,0,0,346,347,5,116,0,0,347,348,5,120,0,0,348, - 349,5,46,0,0,349,350,5,105,0,0,350,351,5,110,0,0,351,352,5,112,0,0,352, - 353,5,117,0,0,353,354,5,116,0,0,354,355,5,115,0,0,355,68,1,0,0,0,356,357, - 5,46,0,0,357,358,5,111,0,0,358,359,5,117,0,0,359,360,5,116,0,0,360,361, - 5,112,0,0,361,362,5,111,0,0,362,363,5,105,0,0,363,364,5,110,0,0,364,365, - 5,116,0,0,365,366,5,84,0,0,366,367,5,114,0,0,367,368,5,97,0,0,368,369,5, - 110,0,0,369,370,5,115,0,0,370,371,5,97,0,0,371,372,5,99,0,0,372,373,5,116, - 0,0,373,374,5,105,0,0,374,375,5,111,0,0,375,376,5,110,0,0,376,377,5,72, - 0,0,377,378,5,97,0,0,378,379,5,115,0,0,379,380,5,104,0,0,380,70,1,0,0,0, - 381,382,5,46,0,0,382,383,5,111,0,0,383,384,5,117,0,0,384,385,5,116,0,0, - 385,386,5,112,0,0,386,387,5,111,0,0,387,388,5,105,0,0,388,389,5,110,0,0, - 389,390,5,116,0,0,390,391,5,73,0,0,391,392,5,110,0,0,392,393,5,100,0,0, - 393,394,5,101,0,0,394,395,5,120,0,0,395,72,1,0,0,0,396,397,5,46,0,0,397, - 398,5,117,0,0,398,399,5,110,0,0,399,400,5,108,0,0,400,401,5,111,0,0,401, - 402,5,99,0,0,402,403,5,107,0,0,403,404,5,105,0,0,404,405,5,110,0,0,405, - 406,5,103,0,0,406,407,5,66,0,0,407,408,5,121,0,0,408,409,5,116,0,0,409, - 410,5,101,0,0,410,411,5,99,0,0,411,412,5,111,0,0,412,413,5,100,0,0,413, - 414,5,101,0,0,414,74,1,0,0,0,415,416,5,46,0,0,416,417,5,115,0,0,417,418, - 5,101,0,0,418,419,5,113,0,0,419,420,5,117,0,0,420,421,5,101,0,0,421,422, - 5,110,0,0,422,423,5,99,0,0,423,424,5,101,0,0,424,425,5,78,0,0,425,426,5, - 117,0,0,426,427,5,109,0,0,427,428,5,98,0,0,428,429,5,101,0,0,429,430,5, - 114,0,0,430,76,1,0,0,0,431,432,5,46,0,0,432,433,5,114,0,0,433,434,5,101, - 0,0,434,435,5,118,0,0,435,436,5,101,0,0,436,437,5,114,0,0,437,438,5,115, - 0,0,438,439,5,101,0,0,439,440,5,40,0,0,440,441,5,41,0,0,441,78,1,0,0,0, - 442,443,5,46,0,0,443,444,5,108,0,0,444,445,5,101,0,0,445,446,5,110,0,0, - 446,447,5,103,0,0,447,448,5,116,0,0,448,449,5,104,0,0,449,80,1,0,0,0,450, - 451,5,46,0,0,451,452,5,115,0,0,452,453,5,112,0,0,453,454,5,108,0,0,454, - 455,5,105,0,0,455,456,5,116,0,0,456,82,1,0,0,0,457,458,5,46,0,0,458,459, - 5,115,0,0,459,460,5,108,0,0,460,461,5,105,0,0,461,462,5,99,0,0,462,463, - 5,101,0,0,463,84,1,0,0,0,464,465,5,33,0,0,465,86,1,0,0,0,466,467,5,45,0, - 0,467,88,1,0,0,0,468,469,5,42,0,0,469,90,1,0,0,0,470,471,5,47,0,0,471,92, - 1,0,0,0,472,473,5,37,0,0,473,94,1,0,0,0,474,475,5,43,0,0,475,96,1,0,0,0, - 476,477,5,62,0,0,477,478,5,62,0,0,478,98,1,0,0,0,479,480,5,60,0,0,480,481, - 5,60,0,0,481,100,1,0,0,0,482,483,5,61,0,0,483,484,5,61,0,0,484,102,1,0, - 0,0,485,486,5,33,0,0,486,487,5,61,0,0,487,104,1,0,0,0,488,489,5,38,0,0, - 489,106,1,0,0,0,490,491,5,124,0,0,491,108,1,0,0,0,492,493,5,38,0,0,493, - 494,5,38,0,0,494,110,1,0,0,0,495,496,5,124,0,0,496,497,5,124,0,0,497,112, - 1,0,0,0,498,499,5,99,0,0,499,500,5,111,0,0,500,501,5,110,0,0,501,502,5, - 115,0,0,502,503,5,116,0,0,503,504,5,97,0,0,504,505,5,110,0,0,505,506,5, - 116,0,0,506,114,1,0,0,0,507,509,7,0,0,0,508,507,1,0,0,0,509,510,1,0,0,0, - 510,508,1,0,0,0,510,511,1,0,0,0,511,512,1,0,0,0,512,514,5,46,0,0,513,515, - 7,0,0,0,514,513,1,0,0,0,515,516,1,0,0,0,516,514,1,0,0,0,516,517,1,0,0,0, - 517,518,1,0,0,0,518,520,5,46,0,0,519,521,7,0,0,0,520,519,1,0,0,0,521,522, - 1,0,0,0,522,520,1,0,0,0,522,523,1,0,0,0,523,116,1,0,0,0,524,525,5,116,0, - 0,525,526,5,114,0,0,526,527,5,117,0,0,527,534,5,101,0,0,528,529,5,102,0, - 0,529,530,5,97,0,0,530,531,5,108,0,0,531,532,5,115,0,0,532,534,5,101,0, - 0,533,524,1,0,0,0,533,528,1,0,0,0,534,118,1,0,0,0,535,536,5,115,0,0,536, - 537,5,97,0,0,537,538,5,116,0,0,538,539,5,111,0,0,539,540,5,115,0,0,540, - 541,5,104,0,0,541,542,5,105,0,0,542,593,5,115,0,0,543,544,5,115,0,0,544, - 545,5,97,0,0,545,546,5,116,0,0,546,593,5,115,0,0,547,548,5,102,0,0,548, - 549,5,105,0,0,549,550,5,110,0,0,550,551,5,110,0,0,551,552,5,101,0,0,552, - 593,5,121,0,0,553,554,5,98,0,0,554,555,5,105,0,0,555,556,5,116,0,0,556, - 593,5,115,0,0,557,558,5,98,0,0,558,559,5,105,0,0,559,560,5,116,0,0,560, - 561,5,99,0,0,561,562,5,111,0,0,562,563,5,105,0,0,563,593,5,110,0,0,564, - 565,5,115,0,0,565,566,5,101,0,0,566,567,5,99,0,0,567,568,5,111,0,0,568, - 569,5,110,0,0,569,570,5,100,0,0,570,593,5,115,0,0,571,572,5,109,0,0,572, - 573,5,105,0,0,573,574,5,110,0,0,574,575,5,117,0,0,575,576,5,116,0,0,576, - 577,5,101,0,0,577,593,5,115,0,0,578,579,5,104,0,0,579,580,5,111,0,0,580, - 581,5,117,0,0,581,582,5,114,0,0,582,593,5,115,0,0,583,584,5,100,0,0,584, - 585,5,97,0,0,585,586,5,121,0,0,586,593,5,115,0,0,587,588,5,119,0,0,588, - 589,5,101,0,0,589,590,5,101,0,0,590,591,5,107,0,0,591,593,5,115,0,0,592, - 535,1,0,0,0,592,543,1,0,0,0,592,547,1,0,0,0,592,553,1,0,0,0,592,557,1,0, - 0,0,592,564,1,0,0,0,592,571,1,0,0,0,592,578,1,0,0,0,592,583,1,0,0,0,592, - 587,1,0,0,0,593,120,1,0,0,0,594,596,5,45,0,0,595,594,1,0,0,0,595,596,1, - 0,0,0,596,597,1,0,0,0,597,599,3,123,61,0,598,600,3,125,62,0,599,598,1,0, - 0,0,599,600,1,0,0,0,600,122,1,0,0,0,601,603,7,0,0,0,602,601,1,0,0,0,603, - 604,1,0,0,0,604,602,1,0,0,0,604,605,1,0,0,0,605,614,1,0,0,0,606,608,5,95, - 0,0,607,609,7,0,0,0,608,607,1,0,0,0,609,610,1,0,0,0,610,608,1,0,0,0,610, - 611,1,0,0,0,611,613,1,0,0,0,612,606,1,0,0,0,613,616,1,0,0,0,614,612,1,0, - 0,0,614,615,1,0,0,0,615,124,1,0,0,0,616,614,1,0,0,0,617,618,7,1,0,0,618, - 619,3,123,61,0,619,126,1,0,0,0,620,621,5,105,0,0,621,622,5,110,0,0,622, - 650,5,116,0,0,623,624,5,98,0,0,624,625,5,111,0,0,625,626,5,111,0,0,626, - 650,5,108,0,0,627,628,5,115,0,0,628,629,5,116,0,0,629,630,5,114,0,0,630, - 631,5,105,0,0,631,632,5,110,0,0,632,650,5,103,0,0,633,634,5,112,0,0,634, - 635,5,117,0,0,635,636,5,98,0,0,636,637,5,107,0,0,637,638,5,101,0,0,638, - 650,5,121,0,0,639,640,5,115,0,0,640,641,5,105,0,0,641,650,5,103,0,0,642, - 643,5,100,0,0,643,644,5,97,0,0,644,645,5,116,0,0,645,646,5,97,0,0,646,647, - 5,115,0,0,647,648,5,105,0,0,648,650,5,103,0,0,649,620,1,0,0,0,649,623,1, - 0,0,0,649,627,1,0,0,0,649,633,1,0,0,0,649,639,1,0,0,0,649,642,1,0,0,0,650, - 128,1,0,0,0,651,652,5,98,0,0,652,653,5,121,0,0,653,654,5,116,0,0,654,655, - 5,101,0,0,655,656,5,115,0,0,656,130,1,0,0,0,657,658,5,98,0,0,658,659,5, - 121,0,0,659,660,5,116,0,0,660,661,5,101,0,0,661,662,5,115,0,0,662,663,1, - 0,0,0,663,669,3,133,66,0,664,665,5,98,0,0,665,666,5,121,0,0,666,667,5,116, - 0,0,667,669,5,101,0,0,668,657,1,0,0,0,668,664,1,0,0,0,669,132,1,0,0,0,670, - 674,7,2,0,0,671,673,7,0,0,0,672,671,1,0,0,0,673,676,1,0,0,0,674,672,1,0, - 0,0,674,675,1,0,0,0,675,134,1,0,0,0,676,674,1,0,0,0,677,683,5,34,0,0,678, - 679,5,92,0,0,679,682,5,34,0,0,680,682,8,3,0,0,681,678,1,0,0,0,681,680,1, - 0,0,0,682,685,1,0,0,0,683,684,1,0,0,0,683,681,1,0,0,0,684,686,1,0,0,0,685, - 683,1,0,0,0,686,698,5,34,0,0,687,693,5,39,0,0,688,689,5,92,0,0,689,692, - 5,39,0,0,690,692,8,4,0,0,691,688,1,0,0,0,691,690,1,0,0,0,692,695,1,0,0, - 0,693,694,1,0,0,0,693,691,1,0,0,0,694,696,1,0,0,0,695,693,1,0,0,0,696,698, - 5,39,0,0,697,677,1,0,0,0,697,687,1,0,0,0,698,136,1,0,0,0,699,700,5,100, - 0,0,700,701,5,97,0,0,701,702,5,116,0,0,702,703,5,101,0,0,703,704,5,40,0, - 0,704,705,1,0,0,0,705,706,3,135,67,0,706,707,5,41,0,0,707,138,1,0,0,0,708, - 709,5,48,0,0,709,713,7,5,0,0,710,712,7,6,0,0,711,710,1,0,0,0,712,715,1, - 0,0,0,713,711,1,0,0,0,713,714,1,0,0,0,714,140,1,0,0,0,715,713,1,0,0,0,716, - 717,5,116,0,0,717,718,5,104,0,0,718,719,5,105,0,0,719,720,5,115,0,0,720, - 721,5,46,0,0,721,722,5,97,0,0,722,723,5,103,0,0,723,732,5,101,0,0,724,725, - 5,116,0,0,725,726,5,120,0,0,726,727,5,46,0,0,727,728,5,116,0,0,728,729, - 5,105,0,0,729,730,5,109,0,0,730,732,5,101,0,0,731,716,1,0,0,0,731,724,1, - 0,0,0,732,142,1,0,0,0,733,734,5,117,0,0,734,735,5,110,0,0,735,736,5,115, - 0,0,736,737,5,97,0,0,737,738,5,102,0,0,738,739,5,101,0,0,739,740,5,95,0, - 0,740,741,5,105,0,0,741,742,5,110,0,0,742,782,5,116,0,0,743,744,5,117,0, - 0,744,745,5,110,0,0,745,746,5,115,0,0,746,747,5,97,0,0,747,748,5,102,0, - 0,748,749,5,101,0,0,749,750,5,95,0,0,750,751,5,98,0,0,751,752,5,111,0,0, - 752,753,5,111,0,0,753,782,5,108,0,0,754,755,5,117,0,0,755,756,5,110,0,0, - 756,757,5,115,0,0,757,758,5,97,0,0,758,759,5,102,0,0,759,760,5,101,0,0, - 760,761,5,95,0,0,761,762,5,98,0,0,762,763,5,121,0,0,763,764,5,116,0,0,764, - 765,5,101,0,0,765,766,5,115,0,0,766,768,1,0,0,0,767,769,3,133,66,0,768, - 767,1,0,0,0,768,769,1,0,0,0,769,782,1,0,0,0,770,771,5,117,0,0,771,772,5, - 110,0,0,772,773,5,115,0,0,773,774,5,97,0,0,774,775,5,102,0,0,775,776,5, - 101,0,0,776,777,5,95,0,0,777,778,5,98,0,0,778,779,5,121,0,0,779,780,5,116, - 0,0,780,782,5,101,0,0,781,733,1,0,0,0,781,743,1,0,0,0,781,754,1,0,0,0,781, - 770,1,0,0,0,782,144,1,0,0,0,783,784,5,116,0,0,784,785,5,104,0,0,785,786, - 5,105,0,0,786,787,5,115,0,0,787,788,5,46,0,0,788,789,5,97,0,0,789,790,5, - 99,0,0,790,791,5,116,0,0,791,792,5,105,0,0,792,793,5,118,0,0,793,794,5, - 101,0,0,794,795,5,73,0,0,795,796,5,110,0,0,796,797,5,112,0,0,797,798,5, - 117,0,0,798,799,5,116,0,0,799,800,5,73,0,0,800,801,5,110,0,0,801,802,5, - 100,0,0,802,803,5,101,0,0,803,878,5,120,0,0,804,805,5,116,0,0,805,806,5, - 104,0,0,806,807,5,105,0,0,807,808,5,115,0,0,808,809,5,46,0,0,809,810,5, - 97,0,0,810,811,5,99,0,0,811,812,5,116,0,0,812,813,5,105,0,0,813,814,5,118, - 0,0,814,815,5,101,0,0,815,816,5,66,0,0,816,817,5,121,0,0,817,818,5,116, - 0,0,818,819,5,101,0,0,819,820,5,99,0,0,820,821,5,111,0,0,821,822,5,100, - 0,0,822,878,5,101,0,0,823,824,5,116,0,0,824,825,5,120,0,0,825,826,5,46, - 0,0,826,827,5,105,0,0,827,828,5,110,0,0,828,829,5,112,0,0,829,830,5,117, - 0,0,830,831,5,116,0,0,831,832,5,115,0,0,832,833,5,46,0,0,833,834,5,108, - 0,0,834,835,5,101,0,0,835,836,5,110,0,0,836,837,5,103,0,0,837,838,5,116, - 0,0,838,878,5,104,0,0,839,840,5,116,0,0,840,841,5,120,0,0,841,842,5,46, - 0,0,842,843,5,111,0,0,843,844,5,117,0,0,844,845,5,116,0,0,845,846,5,112, - 0,0,846,847,5,117,0,0,847,848,5,116,0,0,848,849,5,115,0,0,849,850,5,46, - 0,0,850,851,5,108,0,0,851,852,5,101,0,0,852,853,5,110,0,0,853,854,5,103, - 0,0,854,855,5,116,0,0,855,878,5,104,0,0,856,857,5,116,0,0,857,858,5,120, - 0,0,858,859,5,46,0,0,859,860,5,118,0,0,860,861,5,101,0,0,861,862,5,114, - 0,0,862,863,5,115,0,0,863,864,5,105,0,0,864,865,5,111,0,0,865,878,5,110, - 0,0,866,867,5,116,0,0,867,868,5,120,0,0,868,869,5,46,0,0,869,870,5,108, - 0,0,870,871,5,111,0,0,871,872,5,99,0,0,872,873,5,107,0,0,873,874,5,116, - 0,0,874,875,5,105,0,0,875,876,5,109,0,0,876,878,5,101,0,0,877,783,1,0,0, - 0,877,804,1,0,0,0,877,823,1,0,0,0,877,839,1,0,0,0,877,856,1,0,0,0,877,866, - 1,0,0,0,878,146,1,0,0,0,879,883,7,7,0,0,880,882,7,8,0,0,881,880,1,0,0,0, - 882,885,1,0,0,0,883,881,1,0,0,0,883,884,1,0,0,0,884,148,1,0,0,0,885,883, - 1,0,0,0,886,888,7,9,0,0,887,886,1,0,0,0,888,889,1,0,0,0,889,887,1,0,0,0, - 889,890,1,0,0,0,890,891,1,0,0,0,891,892,6,74,0,0,892,150,1,0,0,0,893,894, - 5,47,0,0,894,895,5,42,0,0,895,899,1,0,0,0,896,898,9,0,0,0,897,896,1,0,0, - 0,898,901,1,0,0,0,899,900,1,0,0,0,899,897,1,0,0,0,900,902,1,0,0,0,901,899, - 1,0,0,0,902,903,5,42,0,0,903,904,5,47,0,0,904,905,1,0,0,0,905,906,6,75, - 1,0,906,152,1,0,0,0,907,908,5,47,0,0,908,909,5,47,0,0,909,913,1,0,0,0,910, - 912,8,10,0,0,911,910,1,0,0,0,912,915,1,0,0,0,913,911,1,0,0,0,913,914,1, - 0,0,0,914,916,1,0,0,0,915,913,1,0,0,0,916,917,6,76,1,0,917,154,1,0,0,0, - 28,0,510,516,522,533,592,595,599,604,610,614,649,668,674,681,683,691,693, - 697,713,731,768,781,877,883,889,899,913,2,6,0,0,0,1,0]; + 72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,3,72,779,8,72,1,72, + 1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,3,72,792,8,72,1,73,1, + 73,1,73,1,73,1,73,1,73,1,73,1,73,1,73,1,73,1,73,1,73,1,73,1,73,1,73,1,73, + 1,73,1,73,1,73,1,73,1,73,1,73,1,73,1,73,1,73,1,73,1,73,1,73,1,73,1,73,1, + 73,1,73,1,73,1,73,1,73,1,73,1,73,1,73,1,73,1,73,1,73,1,73,1,73,1,73,1,73, + 1,73,1,73,1,73,1,73,1,73,1,73,1,73,1,73,1,73,1,73,1,73,1,73,1,73,1,73,1, + 73,1,73,1,73,1,73,1,73,1,73,1,73,1,73,1,73,1,73,1,73,1,73,1,73,1,73,1,73, + 1,73,1,73,1,73,1,73,1,73,1,73,1,73,1,73,1,73,1,73,1,73,1,73,1,73,1,73,1, + 73,1,73,1,73,1,73,1,73,1,73,3,73,888,8,73,1,74,1,74,5,74,892,8,74,10,74, + 12,74,895,9,74,1,75,4,75,898,8,75,11,75,12,75,899,1,75,1,75,1,76,1,76,1, + 76,1,76,5,76,908,8,76,10,76,12,76,911,9,76,1,76,1,76,1,76,1,76,1,76,1,77, + 1,77,1,77,1,77,5,77,922,8,77,10,77,12,77,925,9,77,1,77,1,77,3,693,703,909, + 0,78,1,1,3,2,5,3,7,4,9,5,11,6,13,7,15,8,17,9,19,10,21,11,23,12,25,13,27, + 14,29,15,31,16,33,17,35,18,37,19,39,20,41,21,43,22,45,23,47,24,49,25,51, + 26,53,27,55,28,57,29,59,30,61,31,63,32,65,33,67,34,69,35,71,36,73,37,75, + 38,77,39,79,40,81,41,83,42,85,43,87,44,89,45,91,46,93,47,95,48,97,49,99, + 50,101,51,103,52,105,53,107,54,109,55,111,56,113,57,115,58,117,59,119,60, + 121,61,123,62,125,63,127,64,129,65,131,66,133,67,135,68,137,69,139,70,141, + 71,143,72,145,73,147,74,149,75,151,76,153,77,155,78,1,0,11,1,0,48,57,2, + 0,69,69,101,101,1,0,49,57,3,0,10,10,13,13,34,34,3,0,10,10,13,13,39,39,2, + 0,88,88,120,120,3,0,48,57,65,70,97,102,2,0,65,90,97,122,4,0,48,57,65,90, + 95,95,97,122,3,0,9,10,12,13,32,32,2,0,10,10,13,13,972,0,1,1,0,0,0,0,3,1, + 0,0,0,0,5,1,0,0,0,0,7,1,0,0,0,0,9,1,0,0,0,0,11,1,0,0,0,0,13,1,0,0,0,0,15, + 1,0,0,0,0,17,1,0,0,0,0,19,1,0,0,0,0,21,1,0,0,0,0,23,1,0,0,0,0,25,1,0,0, + 0,0,27,1,0,0,0,0,29,1,0,0,0,0,31,1,0,0,0,0,33,1,0,0,0,0,35,1,0,0,0,0,37, + 1,0,0,0,0,39,1,0,0,0,0,41,1,0,0,0,0,43,1,0,0,0,0,45,1,0,0,0,0,47,1,0,0, + 0,0,49,1,0,0,0,0,51,1,0,0,0,0,53,1,0,0,0,0,55,1,0,0,0,0,57,1,0,0,0,0,59, + 1,0,0,0,0,61,1,0,0,0,0,63,1,0,0,0,0,65,1,0,0,0,0,67,1,0,0,0,0,69,1,0,0, + 0,0,71,1,0,0,0,0,73,1,0,0,0,0,75,1,0,0,0,0,77,1,0,0,0,0,79,1,0,0,0,0,81, + 1,0,0,0,0,83,1,0,0,0,0,85,1,0,0,0,0,87,1,0,0,0,0,89,1,0,0,0,0,91,1,0,0, + 0,0,93,1,0,0,0,0,95,1,0,0,0,0,97,1,0,0,0,0,99,1,0,0,0,0,101,1,0,0,0,0,103, + 1,0,0,0,0,105,1,0,0,0,0,107,1,0,0,0,0,109,1,0,0,0,0,111,1,0,0,0,0,113,1, + 0,0,0,0,115,1,0,0,0,0,117,1,0,0,0,0,119,1,0,0,0,0,121,1,0,0,0,0,123,1,0, + 0,0,0,125,1,0,0,0,0,127,1,0,0,0,0,129,1,0,0,0,0,131,1,0,0,0,0,133,1,0,0, + 0,0,135,1,0,0,0,0,137,1,0,0,0,0,139,1,0,0,0,0,141,1,0,0,0,0,143,1,0,0,0, + 0,145,1,0,0,0,0,147,1,0,0,0,0,149,1,0,0,0,0,151,1,0,0,0,0,153,1,0,0,0,0, + 155,1,0,0,0,1,157,1,0,0,0,3,164,1,0,0,0,5,166,1,0,0,0,7,177,1,0,0,0,9,179, + 1,0,0,0,11,181,1,0,0,0,13,184,1,0,0,0,15,186,1,0,0,0,17,188,1,0,0,0,19, + 191,1,0,0,0,21,193,1,0,0,0,23,202,1,0,0,0,25,204,1,0,0,0,27,206,1,0,0,0, + 29,214,1,0,0,0,31,223,1,0,0,0,33,225,1,0,0,0,35,227,1,0,0,0,37,229,1,0, + 0,0,39,237,1,0,0,0,41,249,1,0,0,0,43,252,1,0,0,0,45,257,1,0,0,0,47,260, + 1,0,0,0,49,266,1,0,0,0,51,270,1,0,0,0,53,274,1,0,0,0,55,276,1,0,0,0,57, + 278,1,0,0,0,59,289,1,0,0,0,61,296,1,0,0,0,63,313,1,0,0,0,65,328,1,0,0,0, + 67,343,1,0,0,0,69,356,1,0,0,0,71,366,1,0,0,0,73,391,1,0,0,0,75,406,1,0, + 0,0,77,425,1,0,0,0,79,441,1,0,0,0,81,452,1,0,0,0,83,460,1,0,0,0,85,467, + 1,0,0,0,87,474,1,0,0,0,89,476,1,0,0,0,91,478,1,0,0,0,93,480,1,0,0,0,95, + 482,1,0,0,0,97,484,1,0,0,0,99,486,1,0,0,0,101,489,1,0,0,0,103,492,1,0,0, + 0,105,495,1,0,0,0,107,498,1,0,0,0,109,500,1,0,0,0,111,502,1,0,0,0,113,505, + 1,0,0,0,115,508,1,0,0,0,117,518,1,0,0,0,119,543,1,0,0,0,121,602,1,0,0,0, + 123,605,1,0,0,0,125,612,1,0,0,0,127,627,1,0,0,0,129,659,1,0,0,0,131,661, + 1,0,0,0,133,678,1,0,0,0,135,680,1,0,0,0,137,707,1,0,0,0,139,709,1,0,0,0, + 141,718,1,0,0,0,143,741,1,0,0,0,145,791,1,0,0,0,147,887,1,0,0,0,149,889, + 1,0,0,0,151,897,1,0,0,0,153,903,1,0,0,0,155,917,1,0,0,0,157,158,5,112,0, + 0,158,159,5,114,0,0,159,160,5,97,0,0,160,161,5,103,0,0,161,162,5,109,0, + 0,162,163,5,97,0,0,163,2,1,0,0,0,164,165,5,59,0,0,165,4,1,0,0,0,166,167, + 5,99,0,0,167,168,5,97,0,0,168,169,5,115,0,0,169,170,5,104,0,0,170,171,5, + 115,0,0,171,172,5,99,0,0,172,173,5,114,0,0,173,174,5,105,0,0,174,175,5, + 112,0,0,175,176,5,116,0,0,176,6,1,0,0,0,177,178,5,94,0,0,178,8,1,0,0,0, + 179,180,5,126,0,0,180,10,1,0,0,0,181,182,5,62,0,0,182,183,5,61,0,0,183, + 12,1,0,0,0,184,185,5,62,0,0,185,14,1,0,0,0,186,187,5,60,0,0,187,16,1,0, + 0,0,188,189,5,60,0,0,189,190,5,61,0,0,190,18,1,0,0,0,191,192,5,61,0,0,192, + 20,1,0,0,0,193,194,5,99,0,0,194,195,5,111,0,0,195,196,5,110,0,0,196,197, + 5,116,0,0,197,198,5,114,0,0,198,199,5,97,0,0,199,200,5,99,0,0,200,201,5, + 116,0,0,201,22,1,0,0,0,202,203,5,123,0,0,203,24,1,0,0,0,204,205,5,125,0, + 0,205,26,1,0,0,0,206,207,5,108,0,0,207,208,5,105,0,0,208,209,5,98,0,0,209, + 210,5,114,0,0,210,211,5,97,0,0,211,212,5,114,0,0,212,213,5,121,0,0,213, + 28,1,0,0,0,214,215,5,102,0,0,215,216,5,117,0,0,216,217,5,110,0,0,217,218, + 5,99,0,0,218,219,5,116,0,0,219,220,5,105,0,0,220,221,5,111,0,0,221,222, + 5,110,0,0,222,30,1,0,0,0,223,224,5,40,0,0,224,32,1,0,0,0,225,226,5,44,0, + 0,226,34,1,0,0,0,227,228,5,41,0,0,228,36,1,0,0,0,229,230,5,114,0,0,230, + 231,5,101,0,0,231,232,5,113,0,0,232,233,5,117,0,0,233,234,5,105,0,0,234, + 235,5,114,0,0,235,236,5,101,0,0,236,38,1,0,0,0,237,238,5,99,0,0,238,239, + 5,111,0,0,239,240,5,110,0,0,240,241,5,115,0,0,241,242,5,111,0,0,242,243, + 5,108,0,0,243,244,5,101,0,0,244,245,5,46,0,0,245,246,5,108,0,0,246,247, + 5,111,0,0,247,248,5,103,0,0,248,40,1,0,0,0,249,250,5,105,0,0,250,251,5, + 102,0,0,251,42,1,0,0,0,252,253,5,101,0,0,253,254,5,108,0,0,254,255,5,115, + 0,0,255,256,5,101,0,0,256,44,1,0,0,0,257,258,5,100,0,0,258,259,5,111,0, + 0,259,46,1,0,0,0,260,261,5,119,0,0,261,262,5,104,0,0,262,263,5,105,0,0, + 263,264,5,108,0,0,264,265,5,101,0,0,265,48,1,0,0,0,266,267,5,102,0,0,267, + 268,5,111,0,0,268,269,5,114,0,0,269,50,1,0,0,0,270,271,5,110,0,0,271,272, + 5,101,0,0,272,273,5,119,0,0,273,52,1,0,0,0,274,275,5,91,0,0,275,54,1,0, + 0,0,276,277,5,93,0,0,277,56,1,0,0,0,278,279,5,116,0,0,279,280,5,120,0,0, + 280,281,5,46,0,0,281,282,5,111,0,0,282,283,5,117,0,0,283,284,5,116,0,0, + 284,285,5,112,0,0,285,286,5,117,0,0,286,287,5,116,0,0,287,288,5,115,0,0, + 288,58,1,0,0,0,289,290,5,46,0,0,290,291,5,118,0,0,291,292,5,97,0,0,292, + 293,5,108,0,0,293,294,5,117,0,0,294,295,5,101,0,0,295,60,1,0,0,0,296,297, + 5,46,0,0,297,298,5,108,0,0,298,299,5,111,0,0,299,300,5,99,0,0,300,301,5, + 107,0,0,301,302,5,105,0,0,302,303,5,110,0,0,303,304,5,103,0,0,304,305,5, + 66,0,0,305,306,5,121,0,0,306,307,5,116,0,0,307,308,5,101,0,0,308,309,5, + 99,0,0,309,310,5,111,0,0,310,311,5,100,0,0,311,312,5,101,0,0,312,62,1,0, + 0,0,313,314,5,46,0,0,314,315,5,116,0,0,315,316,5,111,0,0,316,317,5,107, + 0,0,317,318,5,101,0,0,318,319,5,110,0,0,319,320,5,67,0,0,320,321,5,97,0, + 0,321,322,5,116,0,0,322,323,5,101,0,0,323,324,5,103,0,0,324,325,5,111,0, + 0,325,326,5,114,0,0,326,327,5,121,0,0,327,64,1,0,0,0,328,329,5,46,0,0,329, + 330,5,110,0,0,330,331,5,102,0,0,331,332,5,116,0,0,332,333,5,67,0,0,333, + 334,5,111,0,0,334,335,5,109,0,0,335,336,5,109,0,0,336,337,5,105,0,0,337, + 338,5,116,0,0,338,339,5,109,0,0,339,340,5,101,0,0,340,341,5,110,0,0,341, + 342,5,116,0,0,342,66,1,0,0,0,343,344,5,46,0,0,344,345,5,116,0,0,345,346, + 5,111,0,0,346,347,5,107,0,0,347,348,5,101,0,0,348,349,5,110,0,0,349,350, + 5,65,0,0,350,351,5,109,0,0,351,352,5,111,0,0,352,353,5,117,0,0,353,354, + 5,110,0,0,354,355,5,116,0,0,355,68,1,0,0,0,356,357,5,116,0,0,357,358,5, + 120,0,0,358,359,5,46,0,0,359,360,5,105,0,0,360,361,5,110,0,0,361,362,5, + 112,0,0,362,363,5,117,0,0,363,364,5,116,0,0,364,365,5,115,0,0,365,70,1, + 0,0,0,366,367,5,46,0,0,367,368,5,111,0,0,368,369,5,117,0,0,369,370,5,116, + 0,0,370,371,5,112,0,0,371,372,5,111,0,0,372,373,5,105,0,0,373,374,5,110, + 0,0,374,375,5,116,0,0,375,376,5,84,0,0,376,377,5,114,0,0,377,378,5,97,0, + 0,378,379,5,110,0,0,379,380,5,115,0,0,380,381,5,97,0,0,381,382,5,99,0,0, + 382,383,5,116,0,0,383,384,5,105,0,0,384,385,5,111,0,0,385,386,5,110,0,0, + 386,387,5,72,0,0,387,388,5,97,0,0,388,389,5,115,0,0,389,390,5,104,0,0,390, + 72,1,0,0,0,391,392,5,46,0,0,392,393,5,111,0,0,393,394,5,117,0,0,394,395, + 5,116,0,0,395,396,5,112,0,0,396,397,5,111,0,0,397,398,5,105,0,0,398,399, + 5,110,0,0,399,400,5,116,0,0,400,401,5,73,0,0,401,402,5,110,0,0,402,403, + 5,100,0,0,403,404,5,101,0,0,404,405,5,120,0,0,405,74,1,0,0,0,406,407,5, + 46,0,0,407,408,5,117,0,0,408,409,5,110,0,0,409,410,5,108,0,0,410,411,5, + 111,0,0,411,412,5,99,0,0,412,413,5,107,0,0,413,414,5,105,0,0,414,415,5, + 110,0,0,415,416,5,103,0,0,416,417,5,66,0,0,417,418,5,121,0,0,418,419,5, + 116,0,0,419,420,5,101,0,0,420,421,5,99,0,0,421,422,5,111,0,0,422,423,5, + 100,0,0,423,424,5,101,0,0,424,76,1,0,0,0,425,426,5,46,0,0,426,427,5,115, + 0,0,427,428,5,101,0,0,428,429,5,113,0,0,429,430,5,117,0,0,430,431,5,101, + 0,0,431,432,5,110,0,0,432,433,5,99,0,0,433,434,5,101,0,0,434,435,5,78,0, + 0,435,436,5,117,0,0,436,437,5,109,0,0,437,438,5,98,0,0,438,439,5,101,0, + 0,439,440,5,114,0,0,440,78,1,0,0,0,441,442,5,46,0,0,442,443,5,114,0,0,443, + 444,5,101,0,0,444,445,5,118,0,0,445,446,5,101,0,0,446,447,5,114,0,0,447, + 448,5,115,0,0,448,449,5,101,0,0,449,450,5,40,0,0,450,451,5,41,0,0,451,80, + 1,0,0,0,452,453,5,46,0,0,453,454,5,108,0,0,454,455,5,101,0,0,455,456,5, + 110,0,0,456,457,5,103,0,0,457,458,5,116,0,0,458,459,5,104,0,0,459,82,1, + 0,0,0,460,461,5,46,0,0,461,462,5,115,0,0,462,463,5,112,0,0,463,464,5,108, + 0,0,464,465,5,105,0,0,465,466,5,116,0,0,466,84,1,0,0,0,467,468,5,46,0,0, + 468,469,5,115,0,0,469,470,5,108,0,0,470,471,5,105,0,0,471,472,5,99,0,0, + 472,473,5,101,0,0,473,86,1,0,0,0,474,475,5,33,0,0,475,88,1,0,0,0,476,477, + 5,45,0,0,477,90,1,0,0,0,478,479,5,42,0,0,479,92,1,0,0,0,480,481,5,47,0, + 0,481,94,1,0,0,0,482,483,5,37,0,0,483,96,1,0,0,0,484,485,5,43,0,0,485,98, + 1,0,0,0,486,487,5,62,0,0,487,488,5,62,0,0,488,100,1,0,0,0,489,490,5,60, + 0,0,490,491,5,60,0,0,491,102,1,0,0,0,492,493,5,61,0,0,493,494,5,61,0,0, + 494,104,1,0,0,0,495,496,5,33,0,0,496,497,5,61,0,0,497,106,1,0,0,0,498,499, + 5,38,0,0,499,108,1,0,0,0,500,501,5,124,0,0,501,110,1,0,0,0,502,503,5,38, + 0,0,503,504,5,38,0,0,504,112,1,0,0,0,505,506,5,124,0,0,506,507,5,124,0, + 0,507,114,1,0,0,0,508,509,5,99,0,0,509,510,5,111,0,0,510,511,5,110,0,0, + 511,512,5,115,0,0,512,513,5,116,0,0,513,514,5,97,0,0,514,515,5,110,0,0, + 515,516,5,116,0,0,516,116,1,0,0,0,517,519,7,0,0,0,518,517,1,0,0,0,519,520, + 1,0,0,0,520,518,1,0,0,0,520,521,1,0,0,0,521,522,1,0,0,0,522,524,5,46,0, + 0,523,525,7,0,0,0,524,523,1,0,0,0,525,526,1,0,0,0,526,524,1,0,0,0,526,527, + 1,0,0,0,527,528,1,0,0,0,528,530,5,46,0,0,529,531,7,0,0,0,530,529,1,0,0, + 0,531,532,1,0,0,0,532,530,1,0,0,0,532,533,1,0,0,0,533,118,1,0,0,0,534,535, + 5,116,0,0,535,536,5,114,0,0,536,537,5,117,0,0,537,544,5,101,0,0,538,539, + 5,102,0,0,539,540,5,97,0,0,540,541,5,108,0,0,541,542,5,115,0,0,542,544, + 5,101,0,0,543,534,1,0,0,0,543,538,1,0,0,0,544,120,1,0,0,0,545,546,5,115, + 0,0,546,547,5,97,0,0,547,548,5,116,0,0,548,549,5,111,0,0,549,550,5,115, + 0,0,550,551,5,104,0,0,551,552,5,105,0,0,552,603,5,115,0,0,553,554,5,115, + 0,0,554,555,5,97,0,0,555,556,5,116,0,0,556,603,5,115,0,0,557,558,5,102, + 0,0,558,559,5,105,0,0,559,560,5,110,0,0,560,561,5,110,0,0,561,562,5,101, + 0,0,562,603,5,121,0,0,563,564,5,98,0,0,564,565,5,105,0,0,565,566,5,116, + 0,0,566,603,5,115,0,0,567,568,5,98,0,0,568,569,5,105,0,0,569,570,5,116, + 0,0,570,571,5,99,0,0,571,572,5,111,0,0,572,573,5,105,0,0,573,603,5,110, + 0,0,574,575,5,115,0,0,575,576,5,101,0,0,576,577,5,99,0,0,577,578,5,111, + 0,0,578,579,5,110,0,0,579,580,5,100,0,0,580,603,5,115,0,0,581,582,5,109, + 0,0,582,583,5,105,0,0,583,584,5,110,0,0,584,585,5,117,0,0,585,586,5,116, + 0,0,586,587,5,101,0,0,587,603,5,115,0,0,588,589,5,104,0,0,589,590,5,111, + 0,0,590,591,5,117,0,0,591,592,5,114,0,0,592,603,5,115,0,0,593,594,5,100, + 0,0,594,595,5,97,0,0,595,596,5,121,0,0,596,603,5,115,0,0,597,598,5,119, + 0,0,598,599,5,101,0,0,599,600,5,101,0,0,600,601,5,107,0,0,601,603,5,115, + 0,0,602,545,1,0,0,0,602,553,1,0,0,0,602,557,1,0,0,0,602,563,1,0,0,0,602, + 567,1,0,0,0,602,574,1,0,0,0,602,581,1,0,0,0,602,588,1,0,0,0,602,593,1,0, + 0,0,602,597,1,0,0,0,603,122,1,0,0,0,604,606,5,45,0,0,605,604,1,0,0,0,605, + 606,1,0,0,0,606,607,1,0,0,0,607,609,3,125,62,0,608,610,3,127,63,0,609,608, + 1,0,0,0,609,610,1,0,0,0,610,124,1,0,0,0,611,613,7,0,0,0,612,611,1,0,0,0, + 613,614,1,0,0,0,614,612,1,0,0,0,614,615,1,0,0,0,615,624,1,0,0,0,616,618, + 5,95,0,0,617,619,7,0,0,0,618,617,1,0,0,0,619,620,1,0,0,0,620,618,1,0,0, + 0,620,621,1,0,0,0,621,623,1,0,0,0,622,616,1,0,0,0,623,626,1,0,0,0,624,622, + 1,0,0,0,624,625,1,0,0,0,625,126,1,0,0,0,626,624,1,0,0,0,627,628,7,1,0,0, + 628,629,3,125,62,0,629,128,1,0,0,0,630,631,5,105,0,0,631,632,5,110,0,0, + 632,660,5,116,0,0,633,634,5,98,0,0,634,635,5,111,0,0,635,636,5,111,0,0, + 636,660,5,108,0,0,637,638,5,115,0,0,638,639,5,116,0,0,639,640,5,114,0,0, + 640,641,5,105,0,0,641,642,5,110,0,0,642,660,5,103,0,0,643,644,5,112,0,0, + 644,645,5,117,0,0,645,646,5,98,0,0,646,647,5,107,0,0,647,648,5,101,0,0, + 648,660,5,121,0,0,649,650,5,115,0,0,650,651,5,105,0,0,651,660,5,103,0,0, + 652,653,5,100,0,0,653,654,5,97,0,0,654,655,5,116,0,0,655,656,5,97,0,0,656, + 657,5,115,0,0,657,658,5,105,0,0,658,660,5,103,0,0,659,630,1,0,0,0,659,633, + 1,0,0,0,659,637,1,0,0,0,659,643,1,0,0,0,659,649,1,0,0,0,659,652,1,0,0,0, + 660,130,1,0,0,0,661,662,5,98,0,0,662,663,5,121,0,0,663,664,5,116,0,0,664, + 665,5,101,0,0,665,666,5,115,0,0,666,132,1,0,0,0,667,668,5,98,0,0,668,669, + 5,121,0,0,669,670,5,116,0,0,670,671,5,101,0,0,671,672,5,115,0,0,672,673, + 1,0,0,0,673,679,3,135,67,0,674,675,5,98,0,0,675,676,5,121,0,0,676,677,5, + 116,0,0,677,679,5,101,0,0,678,667,1,0,0,0,678,674,1,0,0,0,679,134,1,0,0, + 0,680,684,7,2,0,0,681,683,7,0,0,0,682,681,1,0,0,0,683,686,1,0,0,0,684,682, + 1,0,0,0,684,685,1,0,0,0,685,136,1,0,0,0,686,684,1,0,0,0,687,693,5,34,0, + 0,688,689,5,92,0,0,689,692,5,34,0,0,690,692,8,3,0,0,691,688,1,0,0,0,691, + 690,1,0,0,0,692,695,1,0,0,0,693,694,1,0,0,0,693,691,1,0,0,0,694,696,1,0, + 0,0,695,693,1,0,0,0,696,708,5,34,0,0,697,703,5,39,0,0,698,699,5,92,0,0, + 699,702,5,39,0,0,700,702,8,4,0,0,701,698,1,0,0,0,701,700,1,0,0,0,702,705, + 1,0,0,0,703,704,1,0,0,0,703,701,1,0,0,0,704,706,1,0,0,0,705,703,1,0,0,0, + 706,708,5,39,0,0,707,687,1,0,0,0,707,697,1,0,0,0,708,138,1,0,0,0,709,710, + 5,100,0,0,710,711,5,97,0,0,711,712,5,116,0,0,712,713,5,101,0,0,713,714, + 5,40,0,0,714,715,1,0,0,0,715,716,3,137,68,0,716,717,5,41,0,0,717,140,1, + 0,0,0,718,719,5,48,0,0,719,723,7,5,0,0,720,722,7,6,0,0,721,720,1,0,0,0, + 722,725,1,0,0,0,723,721,1,0,0,0,723,724,1,0,0,0,724,142,1,0,0,0,725,723, + 1,0,0,0,726,727,5,116,0,0,727,728,5,104,0,0,728,729,5,105,0,0,729,730,5, + 115,0,0,730,731,5,46,0,0,731,732,5,97,0,0,732,733,5,103,0,0,733,742,5,101, + 0,0,734,735,5,116,0,0,735,736,5,120,0,0,736,737,5,46,0,0,737,738,5,116, + 0,0,738,739,5,105,0,0,739,740,5,109,0,0,740,742,5,101,0,0,741,726,1,0,0, + 0,741,734,1,0,0,0,742,144,1,0,0,0,743,744,5,117,0,0,744,745,5,110,0,0,745, + 746,5,115,0,0,746,747,5,97,0,0,747,748,5,102,0,0,748,749,5,101,0,0,749, + 750,5,95,0,0,750,751,5,105,0,0,751,752,5,110,0,0,752,792,5,116,0,0,753, + 754,5,117,0,0,754,755,5,110,0,0,755,756,5,115,0,0,756,757,5,97,0,0,757, + 758,5,102,0,0,758,759,5,101,0,0,759,760,5,95,0,0,760,761,5,98,0,0,761,762, + 5,111,0,0,762,763,5,111,0,0,763,792,5,108,0,0,764,765,5,117,0,0,765,766, + 5,110,0,0,766,767,5,115,0,0,767,768,5,97,0,0,768,769,5,102,0,0,769,770, + 5,101,0,0,770,771,5,95,0,0,771,772,5,98,0,0,772,773,5,121,0,0,773,774,5, + 116,0,0,774,775,5,101,0,0,775,776,5,115,0,0,776,778,1,0,0,0,777,779,3,135, + 67,0,778,777,1,0,0,0,778,779,1,0,0,0,779,792,1,0,0,0,780,781,5,117,0,0, + 781,782,5,110,0,0,782,783,5,115,0,0,783,784,5,97,0,0,784,785,5,102,0,0, + 785,786,5,101,0,0,786,787,5,95,0,0,787,788,5,98,0,0,788,789,5,121,0,0,789, + 790,5,116,0,0,790,792,5,101,0,0,791,743,1,0,0,0,791,753,1,0,0,0,791,764, + 1,0,0,0,791,780,1,0,0,0,792,146,1,0,0,0,793,794,5,116,0,0,794,795,5,104, + 0,0,795,796,5,105,0,0,796,797,5,115,0,0,797,798,5,46,0,0,798,799,5,97,0, + 0,799,800,5,99,0,0,800,801,5,116,0,0,801,802,5,105,0,0,802,803,5,118,0, + 0,803,804,5,101,0,0,804,805,5,73,0,0,805,806,5,110,0,0,806,807,5,112,0, + 0,807,808,5,117,0,0,808,809,5,116,0,0,809,810,5,73,0,0,810,811,5,110,0, + 0,811,812,5,100,0,0,812,813,5,101,0,0,813,888,5,120,0,0,814,815,5,116,0, + 0,815,816,5,104,0,0,816,817,5,105,0,0,817,818,5,115,0,0,818,819,5,46,0, + 0,819,820,5,97,0,0,820,821,5,99,0,0,821,822,5,116,0,0,822,823,5,105,0,0, + 823,824,5,118,0,0,824,825,5,101,0,0,825,826,5,66,0,0,826,827,5,121,0,0, + 827,828,5,116,0,0,828,829,5,101,0,0,829,830,5,99,0,0,830,831,5,111,0,0, + 831,832,5,100,0,0,832,888,5,101,0,0,833,834,5,116,0,0,834,835,5,120,0,0, + 835,836,5,46,0,0,836,837,5,105,0,0,837,838,5,110,0,0,838,839,5,112,0,0, + 839,840,5,117,0,0,840,841,5,116,0,0,841,842,5,115,0,0,842,843,5,46,0,0, + 843,844,5,108,0,0,844,845,5,101,0,0,845,846,5,110,0,0,846,847,5,103,0,0, + 847,848,5,116,0,0,848,888,5,104,0,0,849,850,5,116,0,0,850,851,5,120,0,0, + 851,852,5,46,0,0,852,853,5,111,0,0,853,854,5,117,0,0,854,855,5,116,0,0, + 855,856,5,112,0,0,856,857,5,117,0,0,857,858,5,116,0,0,858,859,5,115,0,0, + 859,860,5,46,0,0,860,861,5,108,0,0,861,862,5,101,0,0,862,863,5,110,0,0, + 863,864,5,103,0,0,864,865,5,116,0,0,865,888,5,104,0,0,866,867,5,116,0,0, + 867,868,5,120,0,0,868,869,5,46,0,0,869,870,5,118,0,0,870,871,5,101,0,0, + 871,872,5,114,0,0,872,873,5,115,0,0,873,874,5,105,0,0,874,875,5,111,0,0, + 875,888,5,110,0,0,876,877,5,116,0,0,877,878,5,120,0,0,878,879,5,46,0,0, + 879,880,5,108,0,0,880,881,5,111,0,0,881,882,5,99,0,0,882,883,5,107,0,0, + 883,884,5,116,0,0,884,885,5,105,0,0,885,886,5,109,0,0,886,888,5,101,0,0, + 887,793,1,0,0,0,887,814,1,0,0,0,887,833,1,0,0,0,887,849,1,0,0,0,887,866, + 1,0,0,0,887,876,1,0,0,0,888,148,1,0,0,0,889,893,7,7,0,0,890,892,7,8,0,0, + 891,890,1,0,0,0,892,895,1,0,0,0,893,891,1,0,0,0,893,894,1,0,0,0,894,150, + 1,0,0,0,895,893,1,0,0,0,896,898,7,9,0,0,897,896,1,0,0,0,898,899,1,0,0,0, + 899,897,1,0,0,0,899,900,1,0,0,0,900,901,1,0,0,0,901,902,6,75,0,0,902,152, + 1,0,0,0,903,904,5,47,0,0,904,905,5,42,0,0,905,909,1,0,0,0,906,908,9,0,0, + 0,907,906,1,0,0,0,908,911,1,0,0,0,909,910,1,0,0,0,909,907,1,0,0,0,910,912, + 1,0,0,0,911,909,1,0,0,0,912,913,5,42,0,0,913,914,5,47,0,0,914,915,1,0,0, + 0,915,916,6,76,1,0,916,154,1,0,0,0,917,918,5,47,0,0,918,919,5,47,0,0,919, + 923,1,0,0,0,920,922,8,10,0,0,921,920,1,0,0,0,922,925,1,0,0,0,923,921,1, + 0,0,0,923,924,1,0,0,0,924,926,1,0,0,0,925,923,1,0,0,0,926,927,6,77,1,0, + 927,156,1,0,0,0,28,0,520,526,532,543,602,605,609,614,620,624,659,678,684, + 691,693,701,703,707,723,741,778,791,887,893,899,909,923,2,6,0,0,0,1,0]; private static __ATN: ATN; public static get _ATN(): ATN { diff --git a/packages/cashc/src/grammar/CashScriptParser.ts b/packages/cashc/src/grammar/CashScriptParser.ts index dc7f04a2..20d42f2c 100644 --- a/packages/cashc/src/grammar/CashScriptParser.ts +++ b/packages/cashc/src/grammar/CashScriptParser.ts @@ -1,4 +1,4 @@ -// Generated from src/grammar/CashScript.g4 by ANTLR 4.13.1 +// Generated from src/grammar/CashScript.g4 by ANTLR 4.13.2 // noinspection ES6UnusedImports,JSUnusedGlobalSymbols,JSUnusedLocalSymbols import { @@ -75,64 +75,67 @@ export default class CashScriptParser extends Parser { public static readonly T__54 = 55; public static readonly T__55 = 56; public static readonly T__56 = 57; - public static readonly VersionLiteral = 58; - public static readonly BooleanLiteral = 59; - public static readonly NumberUnit = 60; - public static readonly NumberLiteral = 61; - public static readonly NumberPart = 62; - public static readonly ExponentPart = 63; - public static readonly PrimitiveType = 64; - public static readonly UnboundedBytes = 65; - public static readonly BoundedBytes = 66; - public static readonly Bound = 67; - public static readonly StringLiteral = 68; - public static readonly DateLiteral = 69; - public static readonly HexLiteral = 70; - public static readonly TxVar = 71; - public static readonly UnsafeCast = 72; - public static readonly NullaryOp = 73; - public static readonly Identifier = 74; - public static readonly WHITESPACE = 75; - public static readonly COMMENT = 76; - public static readonly LINE_COMMENT = 77; - public static readonly EOF = Token.EOF; + public static readonly T__57 = 58; + public static readonly VersionLiteral = 59; + public static readonly BooleanLiteral = 60; + public static readonly NumberUnit = 61; + public static readonly NumberLiteral = 62; + public static readonly NumberPart = 63; + public static readonly ExponentPart = 64; + public static readonly PrimitiveType = 65; + public static readonly UnboundedBytes = 66; + public static readonly BoundedBytes = 67; + public static readonly Bound = 68; + public static readonly StringLiteral = 69; + public static readonly DateLiteral = 70; + public static readonly HexLiteral = 71; + public static readonly TxVar = 72; + public static readonly UnsafeCast = 73; + public static readonly NullaryOp = 74; + public static readonly Identifier = 75; + public static readonly WHITESPACE = 76; + public static readonly COMMENT = 77; + public static readonly LINE_COMMENT = 78; + public static override readonly EOF = Token.EOF; public static readonly RULE_sourceFile = 0; - public static readonly RULE_pragmaDirective = 1; - public static readonly RULE_pragmaName = 2; - public static readonly RULE_pragmaValue = 3; - public static readonly RULE_versionConstraint = 4; - public static readonly RULE_versionOperator = 5; - public static readonly RULE_contractDefinition = 6; - public static readonly RULE_functionDefinition = 7; - public static readonly RULE_parameterList = 8; - public static readonly RULE_parameter = 9; - public static readonly RULE_block = 10; - public static readonly RULE_statement = 11; - public static readonly RULE_nonControlStatement = 12; - public static readonly RULE_controlStatement = 13; - public static readonly RULE_variableDefinition = 14; - public static readonly RULE_tupleAssignment = 15; - public static readonly RULE_assignStatement = 16; - public static readonly RULE_timeOpStatement = 17; - public static readonly RULE_requireStatement = 18; - public static readonly RULE_consoleStatement = 19; - public static readonly RULE_ifStatement = 20; - public static readonly RULE_loopStatement = 21; - public static readonly RULE_doWhileStatement = 22; - public static readonly RULE_whileStatement = 23; - public static readonly RULE_forStatement = 24; - public static readonly RULE_forInit = 25; - public static readonly RULE_requireMessage = 26; - public static readonly RULE_consoleParameter = 27; - public static readonly RULE_consoleParameterList = 28; - public static readonly RULE_functionCall = 29; - public static readonly RULE_expressionList = 30; - public static readonly RULE_expression = 31; - public static readonly RULE_modifier = 32; - public static readonly RULE_literal = 33; - public static readonly RULE_numberLiteral = 34; - public static readonly RULE_typeName = 35; - public static readonly RULE_typeCast = 36; + public static readonly RULE_topLevelDefinition = 1; + public static readonly RULE_pragmaDirective = 2; + public static readonly RULE_pragmaName = 3; + public static readonly RULE_pragmaValue = 4; + public static readonly RULE_versionConstraint = 5; + public static readonly RULE_versionOperator = 6; + public static readonly RULE_contractDefinition = 7; + public static readonly RULE_libraryDefinition = 8; + public static readonly RULE_functionDefinition = 9; + public static readonly RULE_parameterList = 10; + public static readonly RULE_parameter = 11; + public static readonly RULE_block = 12; + public static readonly RULE_statement = 13; + public static readonly RULE_nonControlStatement = 14; + public static readonly RULE_controlStatement = 15; + public static readonly RULE_variableDefinition = 16; + public static readonly RULE_tupleAssignment = 17; + public static readonly RULE_assignStatement = 18; + public static readonly RULE_timeOpStatement = 19; + public static readonly RULE_requireStatement = 20; + public static readonly RULE_consoleStatement = 21; + public static readonly RULE_ifStatement = 22; + public static readonly RULE_loopStatement = 23; + public static readonly RULE_doWhileStatement = 24; + public static readonly RULE_whileStatement = 25; + public static readonly RULE_forStatement = 26; + public static readonly RULE_forInit = 27; + public static readonly RULE_requireMessage = 28; + public static readonly RULE_consoleParameter = 29; + public static readonly RULE_consoleParameterList = 30; + public static readonly RULE_functionCall = 31; + public static readonly RULE_expressionList = 32; + public static readonly RULE_expression = 33; + public static readonly RULE_modifier = 34; + public static readonly RULE_literal = 35; + public static readonly RULE_numberLiteral = 36; + public static readonly RULE_typeName = 37; + public static readonly RULE_typeCast = 38; public static readonly literalNames: (string | null)[] = [ null, "'pragma'", "';'", "'cashscript'", "'^'", "'~'", @@ -140,6 +143,7 @@ export default class CashScriptParser extends Parser { "'<'", "'<='", "'='", "'contract'", "'{'", "'}'", + "'library'", "'function'", "'('", "','", "')'", "'require'", @@ -204,7 +208,7 @@ export default class CashScriptParser extends Parser { null, null, null, null, null, null, - "VersionLiteral", + null, "VersionLiteral", "BooleanLiteral", "NumberUnit", "NumberLiteral", @@ -224,14 +228,15 @@ export default class CashScriptParser extends Parser { "LINE_COMMENT" ]; // tslint:disable:no-trailing-whitespace public static readonly ruleNames: string[] = [ - "sourceFile", "pragmaDirective", "pragmaName", "pragmaValue", "versionConstraint", - "versionOperator", "contractDefinition", "functionDefinition", "parameterList", - "parameter", "block", "statement", "nonControlStatement", "controlStatement", - "variableDefinition", "tupleAssignment", "assignStatement", "timeOpStatement", - "requireStatement", "consoleStatement", "ifStatement", "loopStatement", - "doWhileStatement", "whileStatement", "forStatement", "forInit", "requireMessage", - "consoleParameter", "consoleParameterList", "functionCall", "expressionList", - "expression", "modifier", "literal", "numberLiteral", "typeName", "typeCast", + "sourceFile", "topLevelDefinition", "pragmaDirective", "pragmaName", "pragmaValue", + "versionConstraint", "versionOperator", "contractDefinition", "libraryDefinition", + "functionDefinition", "parameterList", "parameter", "block", "statement", + "nonControlStatement", "controlStatement", "variableDefinition", "tupleAssignment", + "assignStatement", "timeOpStatement", "requireStatement", "consoleStatement", + "ifStatement", "loopStatement", "doWhileStatement", "whileStatement", + "forStatement", "forInit", "requireMessage", "consoleParameter", "consoleParameterList", + "functionCall", "expressionList", "expression", "modifier", "literal", + "numberLiteral", "typeName", "typeCast", ]; public get grammarFileName(): string { return "CashScript.g4"; } public get literalNames(): (string | null)[] { return CashScriptParser.literalNames; } @@ -255,23 +260,23 @@ export default class CashScriptParser extends Parser { try { this.enterOuterAlt(localctx, 1); { - this.state = 77; + this.state = 81; this._errHandler.sync(this); _la = this._input.LA(1); while (_la===1) { { { - this.state = 74; + this.state = 78; this.pragmaDirective(); } } - this.state = 79; + this.state = 83; this._errHandler.sync(this); _la = this._input.LA(1); } - this.state = 80; - this.contractDefinition(); - this.state = 81; + this.state = 84; + this.topLevelDefinition(); + this.state = 85; this.match(CashScriptParser.EOF); } } @@ -290,19 +295,59 @@ export default class CashScriptParser extends Parser { return localctx; } // @RuleVersion(0) + public topLevelDefinition(): TopLevelDefinitionContext { + let localctx: TopLevelDefinitionContext = new TopLevelDefinitionContext(this, this._ctx, this.state); + this.enterRule(localctx, 2, CashScriptParser.RULE_topLevelDefinition); + try { + this.state = 89; + this._errHandler.sync(this); + switch (this._input.LA(1)) { + case 11: + this.enterOuterAlt(localctx, 1); + { + this.state = 87; + this.contractDefinition(); + } + break; + case 14: + this.enterOuterAlt(localctx, 2); + { + this.state = 88; + this.libraryDefinition(); + } + break; + default: + throw new NoViableAltException(this); + } + } + catch (re) { + if (re instanceof RecognitionException) { + localctx.exception = re; + this._errHandler.reportError(this, re); + this._errHandler.recover(this, re); + } else { + throw re; + } + } + finally { + this.exitRule(); + } + return localctx; + } + // @RuleVersion(0) public pragmaDirective(): PragmaDirectiveContext { let localctx: PragmaDirectiveContext = new PragmaDirectiveContext(this, this._ctx, this.state); - this.enterRule(localctx, 2, CashScriptParser.RULE_pragmaDirective); + this.enterRule(localctx, 4, CashScriptParser.RULE_pragmaDirective); try { this.enterOuterAlt(localctx, 1); { - this.state = 83; + this.state = 91; this.match(CashScriptParser.T__0); - this.state = 84; + this.state = 92; this.pragmaName(); - this.state = 85; + this.state = 93; this.pragmaValue(); - this.state = 86; + this.state = 94; this.match(CashScriptParser.T__1); } } @@ -323,11 +368,11 @@ export default class CashScriptParser extends Parser { // @RuleVersion(0) public pragmaName(): PragmaNameContext { let localctx: PragmaNameContext = new PragmaNameContext(this, this._ctx, this.state); - this.enterRule(localctx, 4, CashScriptParser.RULE_pragmaName); + this.enterRule(localctx, 6, CashScriptParser.RULE_pragmaName); try { this.enterOuterAlt(localctx, 1); { - this.state = 88; + this.state = 96; this.match(CashScriptParser.T__2); } } @@ -348,19 +393,19 @@ export default class CashScriptParser extends Parser { // @RuleVersion(0) public pragmaValue(): PragmaValueContext { let localctx: PragmaValueContext = new PragmaValueContext(this, this._ctx, this.state); - this.enterRule(localctx, 6, CashScriptParser.RULE_pragmaValue); + this.enterRule(localctx, 8, CashScriptParser.RULE_pragmaValue); let _la: number; try { this.enterOuterAlt(localctx, 1); { - this.state = 90; + this.state = 98; this.versionConstraint(); - this.state = 92; + this.state = 100; this._errHandler.sync(this); _la = this._input.LA(1); - if ((((_la) & ~0x1F) === 0 && ((1 << _la) & 2032) !== 0) || _la===58) { + if ((((_la) & ~0x1F) === 0 && ((1 << _la) & 2032) !== 0) || _la===59) { { - this.state = 91; + this.state = 99; this.versionConstraint(); } } @@ -384,22 +429,22 @@ export default class CashScriptParser extends Parser { // @RuleVersion(0) public versionConstraint(): VersionConstraintContext { let localctx: VersionConstraintContext = new VersionConstraintContext(this, this._ctx, this.state); - this.enterRule(localctx, 8, CashScriptParser.RULE_versionConstraint); + this.enterRule(localctx, 10, CashScriptParser.RULE_versionConstraint); let _la: number; try { this.enterOuterAlt(localctx, 1); { - this.state = 95; + this.state = 103; this._errHandler.sync(this); _la = this._input.LA(1); if ((((_la) & ~0x1F) === 0 && ((1 << _la) & 2032) !== 0)) { { - this.state = 94; + this.state = 102; this.versionOperator(); } } - this.state = 97; + this.state = 105; this.match(CashScriptParser.VersionLiteral); } } @@ -420,12 +465,12 @@ export default class CashScriptParser extends Parser { // @RuleVersion(0) public versionOperator(): VersionOperatorContext { let localctx: VersionOperatorContext = new VersionOperatorContext(this, this._ctx, this.state); - this.enterRule(localctx, 10, CashScriptParser.RULE_versionOperator); + this.enterRule(localctx, 12, CashScriptParser.RULE_versionOperator); let _la: number; try { this.enterOuterAlt(localctx, 1); { - this.state = 99; + this.state = 107; _la = this._input.LA(1); if(!((((_la) & ~0x1F) === 0 && ((1 << _la) & 2032) !== 0))) { this._errHandler.recoverInline(this); @@ -453,34 +498,80 @@ export default class CashScriptParser extends Parser { // @RuleVersion(0) public contractDefinition(): ContractDefinitionContext { let localctx: ContractDefinitionContext = new ContractDefinitionContext(this, this._ctx, this.state); - this.enterRule(localctx, 12, CashScriptParser.RULE_contractDefinition); + this.enterRule(localctx, 14, CashScriptParser.RULE_contractDefinition); let _la: number; try { this.enterOuterAlt(localctx, 1); { - this.state = 101; + this.state = 109; this.match(CashScriptParser.T__10); - this.state = 102; + this.state = 110; this.match(CashScriptParser.Identifier); - this.state = 103; + this.state = 111; this.parameterList(); - this.state = 104; + this.state = 112; this.match(CashScriptParser.T__11); - this.state = 108; + this.state = 116; this._errHandler.sync(this); _la = this._input.LA(1); - while (_la===14) { + while (_la===15) { { { - this.state = 105; + this.state = 113; this.functionDefinition(); } } - this.state = 110; + this.state = 118; this._errHandler.sync(this); _la = this._input.LA(1); } - this.state = 111; + this.state = 119; + this.match(CashScriptParser.T__12); + } + } + catch (re) { + if (re instanceof RecognitionException) { + localctx.exception = re; + this._errHandler.reportError(this, re); + this._errHandler.recover(this, re); + } else { + throw re; + } + } + finally { + this.exitRule(); + } + return localctx; + } + // @RuleVersion(0) + public libraryDefinition(): LibraryDefinitionContext { + let localctx: LibraryDefinitionContext = new LibraryDefinitionContext(this, this._ctx, this.state); + this.enterRule(localctx, 16, CashScriptParser.RULE_libraryDefinition); + let _la: number; + try { + this.enterOuterAlt(localctx, 1); + { + this.state = 121; + this.match(CashScriptParser.T__13); + this.state = 122; + this.match(CashScriptParser.Identifier); + this.state = 123; + this.match(CashScriptParser.T__11); + this.state = 127; + this._errHandler.sync(this); + _la = this._input.LA(1); + while (_la===15) { + { + { + this.state = 124; + this.functionDefinition(); + } + } + this.state = 129; + this._errHandler.sync(this); + _la = this._input.LA(1); + } + this.state = 130; this.match(CashScriptParser.T__12); } } @@ -501,34 +592,34 @@ export default class CashScriptParser extends Parser { // @RuleVersion(0) public functionDefinition(): FunctionDefinitionContext { let localctx: FunctionDefinitionContext = new FunctionDefinitionContext(this, this._ctx, this.state); - this.enterRule(localctx, 14, CashScriptParser.RULE_functionDefinition); + this.enterRule(localctx, 18, CashScriptParser.RULE_functionDefinition); let _la: number; try { this.enterOuterAlt(localctx, 1); { - this.state = 113; - this.match(CashScriptParser.T__13); - this.state = 114; + this.state = 132; + this.match(CashScriptParser.T__14); + this.state = 133; this.match(CashScriptParser.Identifier); - this.state = 115; + this.state = 134; this.parameterList(); - this.state = 116; + this.state = 135; this.match(CashScriptParser.T__11); - this.state = 120; + this.state = 139; this._errHandler.sync(this); _la = this._input.LA(1); - while ((((_la) & ~0x1F) === 0 && ((1 << _la) & 31195136) !== 0) || ((((_la - 64)) & ~0x1F) === 0 && ((1 << (_la - 64)) & 1031) !== 0)) { + while ((((_la) & ~0x1F) === 0 && ((1 << _la) & 62390272) !== 0) || ((((_la - 65)) & ~0x1F) === 0 && ((1 << (_la - 65)) & 1031) !== 0)) { { { - this.state = 117; + this.state = 136; this.statement(); } } - this.state = 122; + this.state = 141; this._errHandler.sync(this); _la = this._input.LA(1); } - this.state = 123; + this.state = 142; this.match(CashScriptParser.T__12); } } @@ -549,54 +640,54 @@ export default class CashScriptParser extends Parser { // @RuleVersion(0) public parameterList(): ParameterListContext { let localctx: ParameterListContext = new ParameterListContext(this, this._ctx, this.state); - this.enterRule(localctx, 16, CashScriptParser.RULE_parameterList); + this.enterRule(localctx, 20, CashScriptParser.RULE_parameterList); let _la: number; try { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 125; - this.match(CashScriptParser.T__14); - this.state = 137; + this.state = 144; + this.match(CashScriptParser.T__15); + this.state = 156; this._errHandler.sync(this); _la = this._input.LA(1); - if (((((_la - 64)) & ~0x1F) === 0 && ((1 << (_la - 64)) & 7) !== 0)) { + if (((((_la - 65)) & ~0x1F) === 0 && ((1 << (_la - 65)) & 7) !== 0)) { { - this.state = 126; + this.state = 145; this.parameter(); - this.state = 131; + this.state = 150; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 5, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 7, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 127; - this.match(CashScriptParser.T__15); - this.state = 128; + this.state = 146; + this.match(CashScriptParser.T__16); + this.state = 147; this.parameter(); } } } - this.state = 133; + this.state = 152; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 5, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 7, this._ctx); } - this.state = 135; + this.state = 154; this._errHandler.sync(this); _la = this._input.LA(1); - if (_la===16) { + if (_la===17) { { - this.state = 134; - this.match(CashScriptParser.T__15); + this.state = 153; + this.match(CashScriptParser.T__16); } } } } - this.state = 139; - this.match(CashScriptParser.T__16); + this.state = 158; + this.match(CashScriptParser.T__17); } } catch (re) { @@ -616,13 +707,13 @@ export default class CashScriptParser extends Parser { // @RuleVersion(0) public parameter(): ParameterContext { let localctx: ParameterContext = new ParameterContext(this, this._ctx, this.state); - this.enterRule(localctx, 18, CashScriptParser.RULE_parameter); + this.enterRule(localctx, 22, CashScriptParser.RULE_parameter); try { this.enterOuterAlt(localctx, 1); { - this.state = 141; + this.state = 160; this.typeName(); - this.state = 142; + this.state = 161; this.match(CashScriptParser.Identifier); } } @@ -643,48 +734,48 @@ export default class CashScriptParser extends Parser { // @RuleVersion(0) public block(): BlockContext { let localctx: BlockContext = new BlockContext(this, this._ctx, this.state); - this.enterRule(localctx, 20, CashScriptParser.RULE_block); + this.enterRule(localctx, 24, CashScriptParser.RULE_block); let _la: number; try { - this.state = 153; + this.state = 172; this._errHandler.sync(this); switch (this._input.LA(1)) { case 12: this.enterOuterAlt(localctx, 1); { - this.state = 144; + this.state = 163; this.match(CashScriptParser.T__11); - this.state = 148; + this.state = 167; this._errHandler.sync(this); _la = this._input.LA(1); - while ((((_la) & ~0x1F) === 0 && ((1 << _la) & 31195136) !== 0) || ((((_la - 64)) & ~0x1F) === 0 && ((1 << (_la - 64)) & 1031) !== 0)) { + while ((((_la) & ~0x1F) === 0 && ((1 << _la) & 62390272) !== 0) || ((((_la - 65)) & ~0x1F) === 0 && ((1 << (_la - 65)) & 1031) !== 0)) { { { - this.state = 145; + this.state = 164; this.statement(); } } - this.state = 150; + this.state = 169; this._errHandler.sync(this); _la = this._input.LA(1); } - this.state = 151; + this.state = 170; this.match(CashScriptParser.T__12); } break; - case 18: case 19: case 20: - case 22: + case 21: case 23: case 24: - case 64: + case 25: case 65: case 66: - case 74: + case 67: + case 75: this.enterOuterAlt(localctx, 2); { - this.state = 152; + this.state = 171; this.statement(); } break; @@ -709,32 +800,32 @@ export default class CashScriptParser extends Parser { // @RuleVersion(0) public statement(): StatementContext { let localctx: StatementContext = new StatementContext(this, this._ctx, this.state); - this.enterRule(localctx, 22, CashScriptParser.RULE_statement); + this.enterRule(localctx, 26, CashScriptParser.RULE_statement); try { - this.state = 159; + this.state = 178; this._errHandler.sync(this); switch (this._input.LA(1)) { - case 20: - case 22: + case 21: case 23: case 24: + case 25: this.enterOuterAlt(localctx, 1); { - this.state = 155; + this.state = 174; this.controlStatement(); } break; - case 18: case 19: - case 64: + case 20: case 65: case 66: - case 74: + case 67: + case 75: this.enterOuterAlt(localctx, 2); { - this.state = 156; + this.state = 175; this.nonControlStatement(); - this.state = 157; + this.state = 176; this.match(CashScriptParser.T__1); } break; @@ -759,50 +850,50 @@ export default class CashScriptParser extends Parser { // @RuleVersion(0) public nonControlStatement(): NonControlStatementContext { let localctx: NonControlStatementContext = new NonControlStatementContext(this, this._ctx, this.state); - this.enterRule(localctx, 24, CashScriptParser.RULE_nonControlStatement); + this.enterRule(localctx, 28, CashScriptParser.RULE_nonControlStatement); try { - this.state = 167; + this.state = 186; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 11, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 13, this._ctx) ) { case 1: this.enterOuterAlt(localctx, 1); { - this.state = 161; + this.state = 180; this.variableDefinition(); } break; case 2: this.enterOuterAlt(localctx, 2); { - this.state = 162; + this.state = 181; this.tupleAssignment(); } break; case 3: this.enterOuterAlt(localctx, 3); { - this.state = 163; + this.state = 182; this.assignStatement(); } break; case 4: this.enterOuterAlt(localctx, 4); { - this.state = 164; + this.state = 183; this.timeOpStatement(); } break; case 5: this.enterOuterAlt(localctx, 5); { - this.state = 165; + this.state = 184; this.requireStatement(); } break; case 6: this.enterOuterAlt(localctx, 6); { - this.state = 166; + this.state = 185; this.consoleStatement(); } break; @@ -825,24 +916,24 @@ export default class CashScriptParser extends Parser { // @RuleVersion(0) public controlStatement(): ControlStatementContext { let localctx: ControlStatementContext = new ControlStatementContext(this, this._ctx, this.state); - this.enterRule(localctx, 26, CashScriptParser.RULE_controlStatement); + this.enterRule(localctx, 30, CashScriptParser.RULE_controlStatement); try { - this.state = 171; + this.state = 190; this._errHandler.sync(this); switch (this._input.LA(1)) { - case 20: + case 21: this.enterOuterAlt(localctx, 1); { - this.state = 169; + this.state = 188; this.ifStatement(); } break; - case 22: case 23: case 24: + case 25: this.enterOuterAlt(localctx, 2); { - this.state = 170; + this.state = 189; this.loopStatement(); } break; @@ -867,32 +958,32 @@ export default class CashScriptParser extends Parser { // @RuleVersion(0) public variableDefinition(): VariableDefinitionContext { let localctx: VariableDefinitionContext = new VariableDefinitionContext(this, this._ctx, this.state); - this.enterRule(localctx, 28, CashScriptParser.RULE_variableDefinition); + this.enterRule(localctx, 32, CashScriptParser.RULE_variableDefinition); let _la: number; try { this.enterOuterAlt(localctx, 1); { - this.state = 173; + this.state = 192; this.typeName(); - this.state = 177; + this.state = 196; this._errHandler.sync(this); _la = this._input.LA(1); - while (_la===57) { + while (_la===58) { { { - this.state = 174; + this.state = 193; this.modifier(); } } - this.state = 179; + this.state = 198; this._errHandler.sync(this); _la = this._input.LA(1); } - this.state = 180; + this.state = 199; this.match(CashScriptParser.Identifier); - this.state = 181; + this.state = 200; this.match(CashScriptParser.T__9); - this.state = 182; + this.state = 201; this.expression(0); } } @@ -913,23 +1004,23 @@ export default class CashScriptParser extends Parser { // @RuleVersion(0) public tupleAssignment(): TupleAssignmentContext { let localctx: TupleAssignmentContext = new TupleAssignmentContext(this, this._ctx, this.state); - this.enterRule(localctx, 30, CashScriptParser.RULE_tupleAssignment); + this.enterRule(localctx, 34, CashScriptParser.RULE_tupleAssignment); try { this.enterOuterAlt(localctx, 1); { - this.state = 184; + this.state = 203; this.typeName(); - this.state = 185; + this.state = 204; this.match(CashScriptParser.Identifier); - this.state = 186; - this.match(CashScriptParser.T__15); - this.state = 187; + this.state = 205; + this.match(CashScriptParser.T__16); + this.state = 206; this.typeName(); - this.state = 188; + this.state = 207; this.match(CashScriptParser.Identifier); - this.state = 189; + this.state = 208; this.match(CashScriptParser.T__9); - this.state = 190; + this.state = 209; this.expression(0); } } @@ -950,15 +1041,15 @@ export default class CashScriptParser extends Parser { // @RuleVersion(0) public assignStatement(): AssignStatementContext { let localctx: AssignStatementContext = new AssignStatementContext(this, this._ctx, this.state); - this.enterRule(localctx, 32, CashScriptParser.RULE_assignStatement); + this.enterRule(localctx, 36, CashScriptParser.RULE_assignStatement); try { this.enterOuterAlt(localctx, 1); { - this.state = 192; + this.state = 211; this.match(CashScriptParser.Identifier); - this.state = 193; + this.state = 212; this.match(CashScriptParser.T__9); - this.state = 194; + this.state = 213; this.expression(0); } } @@ -979,35 +1070,35 @@ export default class CashScriptParser extends Parser { // @RuleVersion(0) public timeOpStatement(): TimeOpStatementContext { let localctx: TimeOpStatementContext = new TimeOpStatementContext(this, this._ctx, this.state); - this.enterRule(localctx, 34, CashScriptParser.RULE_timeOpStatement); + this.enterRule(localctx, 38, CashScriptParser.RULE_timeOpStatement); let _la: number; try { this.enterOuterAlt(localctx, 1); { - this.state = 196; - this.match(CashScriptParser.T__17); - this.state = 197; - this.match(CashScriptParser.T__14); - this.state = 198; + this.state = 215; + this.match(CashScriptParser.T__18); + this.state = 216; + this.match(CashScriptParser.T__15); + this.state = 217; this.match(CashScriptParser.TxVar); - this.state = 199; + this.state = 218; this.match(CashScriptParser.T__5); - this.state = 200; + this.state = 219; this.expression(0); - this.state = 203; + this.state = 222; this._errHandler.sync(this); _la = this._input.LA(1); - if (_la===16) { + if (_la===17) { { - this.state = 201; - this.match(CashScriptParser.T__15); - this.state = 202; + this.state = 220; + this.match(CashScriptParser.T__16); + this.state = 221; this.requireMessage(); } } - this.state = 205; - this.match(CashScriptParser.T__16); + this.state = 224; + this.match(CashScriptParser.T__17); } } catch (re) { @@ -1027,31 +1118,31 @@ export default class CashScriptParser extends Parser { // @RuleVersion(0) public requireStatement(): RequireStatementContext { let localctx: RequireStatementContext = new RequireStatementContext(this, this._ctx, this.state); - this.enterRule(localctx, 36, CashScriptParser.RULE_requireStatement); + this.enterRule(localctx, 40, CashScriptParser.RULE_requireStatement); let _la: number; try { this.enterOuterAlt(localctx, 1); { - this.state = 207; - this.match(CashScriptParser.T__17); - this.state = 208; - this.match(CashScriptParser.T__14); - this.state = 209; + this.state = 226; + this.match(CashScriptParser.T__18); + this.state = 227; + this.match(CashScriptParser.T__15); + this.state = 228; this.expression(0); - this.state = 212; + this.state = 231; this._errHandler.sync(this); _la = this._input.LA(1); - if (_la===16) { + if (_la===17) { { - this.state = 210; - this.match(CashScriptParser.T__15); - this.state = 211; + this.state = 229; + this.match(CashScriptParser.T__16); + this.state = 230; this.requireMessage(); } } - this.state = 214; - this.match(CashScriptParser.T__16); + this.state = 233; + this.match(CashScriptParser.T__17); } } catch (re) { @@ -1071,13 +1162,13 @@ export default class CashScriptParser extends Parser { // @RuleVersion(0) public consoleStatement(): ConsoleStatementContext { let localctx: ConsoleStatementContext = new ConsoleStatementContext(this, this._ctx, this.state); - this.enterRule(localctx, 38, CashScriptParser.RULE_consoleStatement); + this.enterRule(localctx, 42, CashScriptParser.RULE_consoleStatement); try { this.enterOuterAlt(localctx, 1); { - this.state = 216; - this.match(CashScriptParser.T__18); - this.state = 217; + this.state = 235; + this.match(CashScriptParser.T__19); + this.state = 236; this.consoleParameterList(); } } @@ -1098,28 +1189,28 @@ export default class CashScriptParser extends Parser { // @RuleVersion(0) public ifStatement(): IfStatementContext { let localctx: IfStatementContext = new IfStatementContext(this, this._ctx, this.state); - this.enterRule(localctx, 40, CashScriptParser.RULE_ifStatement); + this.enterRule(localctx, 44, CashScriptParser.RULE_ifStatement); try { this.enterOuterAlt(localctx, 1); { - this.state = 219; - this.match(CashScriptParser.T__19); - this.state = 220; - this.match(CashScriptParser.T__14); - this.state = 221; + this.state = 238; + this.match(CashScriptParser.T__20); + this.state = 239; + this.match(CashScriptParser.T__15); + this.state = 240; this.expression(0); - this.state = 222; - this.match(CashScriptParser.T__16); - this.state = 223; + this.state = 241; + this.match(CashScriptParser.T__17); + this.state = 242; localctx._ifBlock = this.block(); - this.state = 226; + this.state = 245; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 16, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 18, this._ctx) ) { case 1: { - this.state = 224; - this.match(CashScriptParser.T__20); - this.state = 225; + this.state = 243; + this.match(CashScriptParser.T__21); + this.state = 244; localctx._elseBlock = this.block(); } break; @@ -1143,29 +1234,29 @@ export default class CashScriptParser extends Parser { // @RuleVersion(0) public loopStatement(): LoopStatementContext { let localctx: LoopStatementContext = new LoopStatementContext(this, this._ctx, this.state); - this.enterRule(localctx, 42, CashScriptParser.RULE_loopStatement); + this.enterRule(localctx, 46, CashScriptParser.RULE_loopStatement); try { - this.state = 231; + this.state = 250; this._errHandler.sync(this); switch (this._input.LA(1)) { - case 22: + case 23: this.enterOuterAlt(localctx, 1); { - this.state = 228; + this.state = 247; this.doWhileStatement(); } break; - case 23: + case 24: this.enterOuterAlt(localctx, 2); { - this.state = 229; + this.state = 248; this.whileStatement(); } break; - case 24: + case 25: this.enterOuterAlt(localctx, 3); { - this.state = 230; + this.state = 249; this.forStatement(); } break; @@ -1190,23 +1281,23 @@ export default class CashScriptParser extends Parser { // @RuleVersion(0) public doWhileStatement(): DoWhileStatementContext { let localctx: DoWhileStatementContext = new DoWhileStatementContext(this, this._ctx, this.state); - this.enterRule(localctx, 44, CashScriptParser.RULE_doWhileStatement); + this.enterRule(localctx, 48, CashScriptParser.RULE_doWhileStatement); try { this.enterOuterAlt(localctx, 1); { - this.state = 233; - this.match(CashScriptParser.T__21); - this.state = 234; - this.block(); - this.state = 235; + this.state = 252; this.match(CashScriptParser.T__22); - this.state = 236; - this.match(CashScriptParser.T__14); - this.state = 237; + this.state = 253; + this.block(); + this.state = 254; + this.match(CashScriptParser.T__23); + this.state = 255; + this.match(CashScriptParser.T__15); + this.state = 256; this.expression(0); - this.state = 238; - this.match(CashScriptParser.T__16); - this.state = 239; + this.state = 257; + this.match(CashScriptParser.T__17); + this.state = 258; this.match(CashScriptParser.T__1); } } @@ -1227,19 +1318,19 @@ export default class CashScriptParser extends Parser { // @RuleVersion(0) public whileStatement(): WhileStatementContext { let localctx: WhileStatementContext = new WhileStatementContext(this, this._ctx, this.state); - this.enterRule(localctx, 46, CashScriptParser.RULE_whileStatement); + this.enterRule(localctx, 50, CashScriptParser.RULE_whileStatement); try { this.enterOuterAlt(localctx, 1); { - this.state = 241; - this.match(CashScriptParser.T__22); - this.state = 242; - this.match(CashScriptParser.T__14); - this.state = 243; + this.state = 260; + this.match(CashScriptParser.T__23); + this.state = 261; + this.match(CashScriptParser.T__15); + this.state = 262; this.expression(0); - this.state = 244; - this.match(CashScriptParser.T__16); - this.state = 245; + this.state = 263; + this.match(CashScriptParser.T__17); + this.state = 264; this.block(); } } @@ -1260,27 +1351,27 @@ export default class CashScriptParser extends Parser { // @RuleVersion(0) public forStatement(): ForStatementContext { let localctx: ForStatementContext = new ForStatementContext(this, this._ctx, this.state); - this.enterRule(localctx, 48, CashScriptParser.RULE_forStatement); + this.enterRule(localctx, 52, CashScriptParser.RULE_forStatement); try { this.enterOuterAlt(localctx, 1); { - this.state = 247; - this.match(CashScriptParser.T__23); - this.state = 248; - this.match(CashScriptParser.T__14); - this.state = 249; + this.state = 266; + this.match(CashScriptParser.T__24); + this.state = 267; + this.match(CashScriptParser.T__15); + this.state = 268; this.forInit(); - this.state = 250; + this.state = 269; this.match(CashScriptParser.T__1); - this.state = 251; + this.state = 270; this.expression(0); - this.state = 252; + this.state = 271; this.match(CashScriptParser.T__1); - this.state = 253; + this.state = 272; this.assignStatement(); - this.state = 254; - this.match(CashScriptParser.T__16); - this.state = 255; + this.state = 273; + this.match(CashScriptParser.T__17); + this.state = 274; this.block(); } } @@ -1301,24 +1392,24 @@ export default class CashScriptParser extends Parser { // @RuleVersion(0) public forInit(): ForInitContext { let localctx: ForInitContext = new ForInitContext(this, this._ctx, this.state); - this.enterRule(localctx, 50, CashScriptParser.RULE_forInit); + this.enterRule(localctx, 54, CashScriptParser.RULE_forInit); try { - this.state = 259; + this.state = 278; this._errHandler.sync(this); switch (this._input.LA(1)) { - case 64: case 65: case 66: + case 67: this.enterOuterAlt(localctx, 1); { - this.state = 257; + this.state = 276; this.variableDefinition(); } break; - case 74: + case 75: this.enterOuterAlt(localctx, 2); { - this.state = 258; + this.state = 277; this.assignStatement(); } break; @@ -1343,11 +1434,11 @@ export default class CashScriptParser extends Parser { // @RuleVersion(0) public requireMessage(): RequireMessageContext { let localctx: RequireMessageContext = new RequireMessageContext(this, this._ctx, this.state); - this.enterRule(localctx, 52, CashScriptParser.RULE_requireMessage); + this.enterRule(localctx, 56, CashScriptParser.RULE_requireMessage); try { this.enterOuterAlt(localctx, 1); { - this.state = 261; + this.state = 280; this.match(CashScriptParser.StringLiteral); } } @@ -1368,26 +1459,26 @@ export default class CashScriptParser extends Parser { // @RuleVersion(0) public consoleParameter(): ConsoleParameterContext { let localctx: ConsoleParameterContext = new ConsoleParameterContext(this, this._ctx, this.state); - this.enterRule(localctx, 54, CashScriptParser.RULE_consoleParameter); + this.enterRule(localctx, 58, CashScriptParser.RULE_consoleParameter); try { - this.state = 265; + this.state = 284; this._errHandler.sync(this); switch (this._input.LA(1)) { - case 74: + case 75: this.enterOuterAlt(localctx, 1); { - this.state = 263; + this.state = 282; this.match(CashScriptParser.Identifier); } break; - case 59: - case 61: - case 68: + case 60: + case 62: case 69: case 70: + case 71: this.enterOuterAlt(localctx, 2); { - this.state = 264; + this.state = 283; this.literal(); } break; @@ -1412,54 +1503,54 @@ export default class CashScriptParser extends Parser { // @RuleVersion(0) public consoleParameterList(): ConsoleParameterListContext { let localctx: ConsoleParameterListContext = new ConsoleParameterListContext(this, this._ctx, this.state); - this.enterRule(localctx, 56, CashScriptParser.RULE_consoleParameterList); + this.enterRule(localctx, 60, CashScriptParser.RULE_consoleParameterList); let _la: number; try { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 267; - this.match(CashScriptParser.T__14); - this.state = 279; + this.state = 286; + this.match(CashScriptParser.T__15); + this.state = 298; this._errHandler.sync(this); _la = this._input.LA(1); - if (((((_la - 59)) & ~0x1F) === 0 && ((1 << (_la - 59)) & 36357) !== 0)) { + if (((((_la - 60)) & ~0x1F) === 0 && ((1 << (_la - 60)) & 36357) !== 0)) { { - this.state = 268; + this.state = 287; this.consoleParameter(); - this.state = 273; + this.state = 292; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 20, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 22, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 269; - this.match(CashScriptParser.T__15); - this.state = 270; + this.state = 288; + this.match(CashScriptParser.T__16); + this.state = 289; this.consoleParameter(); } } } - this.state = 275; + this.state = 294; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 20, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 22, this._ctx); } - this.state = 277; + this.state = 296; this._errHandler.sync(this); _la = this._input.LA(1); - if (_la===16) { + if (_la===17) { { - this.state = 276; - this.match(CashScriptParser.T__15); + this.state = 295; + this.match(CashScriptParser.T__16); } } } } - this.state = 281; - this.match(CashScriptParser.T__16); + this.state = 300; + this.match(CashScriptParser.T__17); } } catch (re) { @@ -1479,13 +1570,13 @@ export default class CashScriptParser extends Parser { // @RuleVersion(0) public functionCall(): FunctionCallContext { let localctx: FunctionCallContext = new FunctionCallContext(this, this._ctx, this.state); - this.enterRule(localctx, 58, CashScriptParser.RULE_functionCall); + this.enterRule(localctx, 62, CashScriptParser.RULE_functionCall); try { this.enterOuterAlt(localctx, 1); { - this.state = 283; + this.state = 302; this.match(CashScriptParser.Identifier); - this.state = 284; + this.state = 303; this.expressionList(); } } @@ -1506,54 +1597,54 @@ export default class CashScriptParser extends Parser { // @RuleVersion(0) public expressionList(): ExpressionListContext { let localctx: ExpressionListContext = new ExpressionListContext(this, this._ctx, this.state); - this.enterRule(localctx, 60, CashScriptParser.RULE_expressionList); + this.enterRule(localctx, 64, CashScriptParser.RULE_expressionList); let _la: number; try { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 286; - this.match(CashScriptParser.T__14); - this.state = 298; + this.state = 305; + this.match(CashScriptParser.T__15); + this.state = 317; this._errHandler.sync(this); _la = this._input.LA(1); - if (((((_la - 5)) & ~0x1F) === 0 && ((1 << (_la - 5)) & 548406273) !== 0) || ((((_la - 43)) & ~0x1F) === 0 && ((1 << (_la - 43)) & 3999596547) !== 0)) { + if (((((_la - 5)) & ~0x1F) === 0 && ((1 << (_la - 5)) & 1096812545) !== 0) || ((((_la - 44)) & ~0x1F) === 0 && ((1 << (_la - 44)) & 3999596547) !== 0)) { { - this.state = 287; + this.state = 306; this.expression(0); - this.state = 292; + this.state = 311; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 23, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 25, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 288; - this.match(CashScriptParser.T__15); - this.state = 289; + this.state = 307; + this.match(CashScriptParser.T__16); + this.state = 308; this.expression(0); } } } - this.state = 294; + this.state = 313; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 23, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 25, this._ctx); } - this.state = 296; + this.state = 315; this._errHandler.sync(this); _la = this._input.LA(1); - if (_la===16) { + if (_la===17) { { - this.state = 295; - this.match(CashScriptParser.T__15); + this.state = 314; + this.match(CashScriptParser.T__16); } } } } - this.state = 300; - this.match(CashScriptParser.T__16); + this.state = 319; + this.match(CashScriptParser.T__17); } } catch (re) { @@ -1583,28 +1674,28 @@ export default class CashScriptParser extends Parser { let _parentState: number = this.state; let localctx: ExpressionContext = new ExpressionContext(this, this._ctx, _parentState); let _prevctx: ExpressionContext = localctx; - let _startState: number = 62; - this.enterRecursionRule(localctx, 62, CashScriptParser.RULE_expression, _p); + let _startState: number = 66; + this.enterRecursionRule(localctx, 66, CashScriptParser.RULE_expression, _p); let _la: number; try { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 351; + this.state = 370; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 30, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 32, this._ctx) ) { case 1: { localctx = new ParenthesisedContext(this, localctx); this._ctx = localctx; _prevctx = localctx; - this.state = 303; - this.match(CashScriptParser.T__14); - this.state = 304; + this.state = 322; + this.match(CashScriptParser.T__15); + this.state = 323; this.expression(0); - this.state = 305; - this.match(CashScriptParser.T__16); + this.state = 324; + this.match(CashScriptParser.T__17); } break; case 2: @@ -1612,24 +1703,24 @@ export default class CashScriptParser extends Parser { localctx = new CastContext(this, localctx); this._ctx = localctx; _prevctx = localctx; - this.state = 307; + this.state = 326; this.typeCast(); - this.state = 308; - this.match(CashScriptParser.T__14); - this.state = 309; + this.state = 327; + this.match(CashScriptParser.T__15); + this.state = 328; (localctx as CastContext)._castable = this.expression(0); - this.state = 311; + this.state = 330; this._errHandler.sync(this); _la = this._input.LA(1); - if (_la===16) { + if (_la===17) { { - this.state = 310; - this.match(CashScriptParser.T__15); + this.state = 329; + this.match(CashScriptParser.T__16); } } - this.state = 313; - this.match(CashScriptParser.T__16); + this.state = 332; + this.match(CashScriptParser.T__17); } break; case 3: @@ -1637,7 +1728,7 @@ export default class CashScriptParser extends Parser { localctx = new FunctionCallExpressionContext(this, localctx); this._ctx = localctx; _prevctx = localctx; - this.state = 315; + this.state = 334; this.functionCall(); } break; @@ -1646,11 +1737,11 @@ export default class CashScriptParser extends Parser { localctx = new InstantiationContext(this, localctx); this._ctx = localctx; _prevctx = localctx; - this.state = 316; - this.match(CashScriptParser.T__24); - this.state = 317; + this.state = 335; + this.match(CashScriptParser.T__25); + this.state = 336; this.match(CashScriptParser.Identifier); - this.state = 318; + this.state = 337; this.expressionList(); } break; @@ -1659,18 +1750,18 @@ export default class CashScriptParser extends Parser { localctx = new UnaryIntrospectionOpContext(this, localctx); this._ctx = localctx; _prevctx = localctx; - this.state = 319; - (localctx as UnaryIntrospectionOpContext)._scope = this.match(CashScriptParser.T__27); - this.state = 320; - this.match(CashScriptParser.T__25); - this.state = 321; - this.expression(0); - this.state = 322; + this.state = 338; + (localctx as UnaryIntrospectionOpContext)._scope = this.match(CashScriptParser.T__28); + this.state = 339; this.match(CashScriptParser.T__26); - this.state = 323; + this.state = 340; + this.expression(0); + this.state = 341; + this.match(CashScriptParser.T__27); + this.state = 342; (localctx as UnaryIntrospectionOpContext)._op = this._input.LT(1); _la = this._input.LA(1); - if(!(((((_la - 29)) & ~0x1F) === 0 && ((1 << (_la - 29)) & 31) !== 0))) { + if(!(((((_la - 30)) & ~0x1F) === 0 && ((1 << (_la - 30)) & 31) !== 0))) { (localctx as UnaryIntrospectionOpContext)._op = this._errHandler.recoverInline(this); } else { @@ -1684,18 +1775,18 @@ export default class CashScriptParser extends Parser { localctx = new UnaryIntrospectionOpContext(this, localctx); this._ctx = localctx; _prevctx = localctx; - this.state = 325; - (localctx as UnaryIntrospectionOpContext)._scope = this.match(CashScriptParser.T__33); - this.state = 326; - this.match(CashScriptParser.T__25); - this.state = 327; - this.expression(0); - this.state = 328; + this.state = 344; + (localctx as UnaryIntrospectionOpContext)._scope = this.match(CashScriptParser.T__34); + this.state = 345; this.match(CashScriptParser.T__26); - this.state = 329; + this.state = 346; + this.expression(0); + this.state = 347; + this.match(CashScriptParser.T__27); + this.state = 348; (localctx as UnaryIntrospectionOpContext)._op = this._input.LT(1); _la = this._input.LA(1); - if(!(((((_la - 29)) & ~0x1F) === 0 && ((1 << (_la - 29)) & 991) !== 0))) { + if(!(((((_la - 30)) & ~0x1F) === 0 && ((1 << (_la - 30)) & 991) !== 0))) { (localctx as UnaryIntrospectionOpContext)._op = this._errHandler.recoverInline(this); } else { @@ -1709,17 +1800,17 @@ export default class CashScriptParser extends Parser { localctx = new UnaryOpContext(this, localctx); this._ctx = localctx; _prevctx = localctx; - this.state = 331; + this.state = 350; (localctx as UnaryOpContext)._op = this._input.LT(1); _la = this._input.LA(1); - if(!(_la===5 || _la===43 || _la===44)) { + if(!(_la===5 || _la===44 || _la===45)) { (localctx as UnaryOpContext)._op = this._errHandler.recoverInline(this); } else { this._errHandler.reportMatch(this); this.consume(); } - this.state = 332; + this.state = 351; this.expression(15); } break; @@ -1728,48 +1819,48 @@ export default class CashScriptParser extends Parser { localctx = new ArrayContext(this, localctx); this._ctx = localctx; _prevctx = localctx; - this.state = 333; - this.match(CashScriptParser.T__25); - this.state = 345; + this.state = 352; + this.match(CashScriptParser.T__26); + this.state = 364; this._errHandler.sync(this); _la = this._input.LA(1); - if (((((_la - 5)) & ~0x1F) === 0 && ((1 << (_la - 5)) & 548406273) !== 0) || ((((_la - 43)) & ~0x1F) === 0 && ((1 << (_la - 43)) & 3999596547) !== 0)) { + if (((((_la - 5)) & ~0x1F) === 0 && ((1 << (_la - 5)) & 1096812545) !== 0) || ((((_la - 44)) & ~0x1F) === 0 && ((1 << (_la - 44)) & 3999596547) !== 0)) { { - this.state = 334; + this.state = 353; this.expression(0); - this.state = 339; + this.state = 358; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 27, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 29, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 335; - this.match(CashScriptParser.T__15); - this.state = 336; + this.state = 354; + this.match(CashScriptParser.T__16); + this.state = 355; this.expression(0); } } } - this.state = 341; + this.state = 360; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 27, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 29, this._ctx); } - this.state = 343; + this.state = 362; this._errHandler.sync(this); _la = this._input.LA(1); - if (_la===16) { + if (_la===17) { { - this.state = 342; - this.match(CashScriptParser.T__15); + this.state = 361; + this.match(CashScriptParser.T__16); } } } } - this.state = 347; - this.match(CashScriptParser.T__26); + this.state = 366; + this.match(CashScriptParser.T__27); } break; case 9: @@ -1777,7 +1868,7 @@ export default class CashScriptParser extends Parser { localctx = new NullaryOpContext(this, localctx); this._ctx = localctx; _prevctx = localctx; - this.state = 348; + this.state = 367; this.match(CashScriptParser.NullaryOp); } break; @@ -1786,7 +1877,7 @@ export default class CashScriptParser extends Parser { localctx = new IdentifierContext(this, localctx); this._ctx = localctx; _prevctx = localctx; - this.state = 349; + this.state = 368; this.match(CashScriptParser.Identifier); } break; @@ -1795,15 +1886,15 @@ export default class CashScriptParser extends Parser { localctx = new LiteralExpressionContext(this, localctx); this._ctx = localctx; _prevctx = localctx; - this.state = 350; + this.state = 369; this.literal(); } break; } this._ctx.stop = this._input.LT(-1); - this.state = 405; + this.state = 424; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 32, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 34, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { if (this._parseListeners != null) { @@ -1811,29 +1902,29 @@ export default class CashScriptParser extends Parser { } _prevctx = localctx; { - this.state = 403; + this.state = 422; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 31, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 33, this._ctx) ) { case 1: { localctx = new BinaryOpContext(this, new ExpressionContext(this, _parentctx, _parentState)); (localctx as BinaryOpContext)._left = _prevctx; this.pushNewRecursionContext(localctx, _startState, CashScriptParser.RULE_expression); - this.state = 353; + this.state = 372; if (!(this.precpred(this._ctx, 14))) { throw this.createFailedPredicateException("this.precpred(this._ctx, 14)"); } - this.state = 354; + this.state = 373; (localctx as BinaryOpContext)._op = this._input.LT(1); _la = this._input.LA(1); - if(!(((((_la - 45)) & ~0x1F) === 0 && ((1 << (_la - 45)) & 7) !== 0))) { + if(!(((((_la - 46)) & ~0x1F) === 0 && ((1 << (_la - 46)) & 7) !== 0))) { (localctx as BinaryOpContext)._op = this._errHandler.recoverInline(this); } else { this._errHandler.reportMatch(this); this.consume(); } - this.state = 355; + this.state = 374; (localctx as BinaryOpContext)._right = this.expression(15); } break; @@ -1842,21 +1933,21 @@ export default class CashScriptParser extends Parser { localctx = new BinaryOpContext(this, new ExpressionContext(this, _parentctx, _parentState)); (localctx as BinaryOpContext)._left = _prevctx; this.pushNewRecursionContext(localctx, _startState, CashScriptParser.RULE_expression); - this.state = 356; + this.state = 375; if (!(this.precpred(this._ctx, 13))) { throw this.createFailedPredicateException("this.precpred(this._ctx, 13)"); } - this.state = 357; + this.state = 376; (localctx as BinaryOpContext)._op = this._input.LT(1); _la = this._input.LA(1); - if(!(_la===44 || _la===48)) { + if(!(_la===45 || _la===49)) { (localctx as BinaryOpContext)._op = this._errHandler.recoverInline(this); } else { this._errHandler.reportMatch(this); this.consume(); } - this.state = 358; + this.state = 377; (localctx as BinaryOpContext)._right = this.expression(14); } break; @@ -1865,21 +1956,21 @@ export default class CashScriptParser extends Parser { localctx = new BinaryOpContext(this, new ExpressionContext(this, _parentctx, _parentState)); (localctx as BinaryOpContext)._left = _prevctx; this.pushNewRecursionContext(localctx, _startState, CashScriptParser.RULE_expression); - this.state = 359; + this.state = 378; if (!(this.precpred(this._ctx, 12))) { throw this.createFailedPredicateException("this.precpred(this._ctx, 12)"); } - this.state = 360; + this.state = 379; (localctx as BinaryOpContext)._op = this._input.LT(1); _la = this._input.LA(1); - if(!(_la===49 || _la===50)) { + if(!(_la===50 || _la===51)) { (localctx as BinaryOpContext)._op = this._errHandler.recoverInline(this); } else { this._errHandler.reportMatch(this); this.consume(); } - this.state = 361; + this.state = 380; (localctx as BinaryOpContext)._right = this.expression(13); } break; @@ -1888,11 +1979,11 @@ export default class CashScriptParser extends Parser { localctx = new BinaryOpContext(this, new ExpressionContext(this, _parentctx, _parentState)); (localctx as BinaryOpContext)._left = _prevctx; this.pushNewRecursionContext(localctx, _startState, CashScriptParser.RULE_expression); - this.state = 362; + this.state = 381; if (!(this.precpred(this._ctx, 11))) { throw this.createFailedPredicateException("this.precpred(this._ctx, 11)"); } - this.state = 363; + this.state = 382; (localctx as BinaryOpContext)._op = this._input.LT(1); _la = this._input.LA(1); if(!((((_la) & ~0x1F) === 0 && ((1 << _la) & 960) !== 0))) { @@ -1902,7 +1993,7 @@ export default class CashScriptParser extends Parser { this._errHandler.reportMatch(this); this.consume(); } - this.state = 364; + this.state = 383; (localctx as BinaryOpContext)._right = this.expression(12); } break; @@ -1911,21 +2002,21 @@ export default class CashScriptParser extends Parser { localctx = new BinaryOpContext(this, new ExpressionContext(this, _parentctx, _parentState)); (localctx as BinaryOpContext)._left = _prevctx; this.pushNewRecursionContext(localctx, _startState, CashScriptParser.RULE_expression); - this.state = 365; + this.state = 384; if (!(this.precpred(this._ctx, 10))) { throw this.createFailedPredicateException("this.precpred(this._ctx, 10)"); } - this.state = 366; + this.state = 385; (localctx as BinaryOpContext)._op = this._input.LT(1); _la = this._input.LA(1); - if(!(_la===51 || _la===52)) { + if(!(_la===52 || _la===53)) { (localctx as BinaryOpContext)._op = this._errHandler.recoverInline(this); } else { this._errHandler.reportMatch(this); this.consume(); } - this.state = 367; + this.state = 386; (localctx as BinaryOpContext)._right = this.expression(11); } break; @@ -1934,13 +2025,13 @@ export default class CashScriptParser extends Parser { localctx = new BinaryOpContext(this, new ExpressionContext(this, _parentctx, _parentState)); (localctx as BinaryOpContext)._left = _prevctx; this.pushNewRecursionContext(localctx, _startState, CashScriptParser.RULE_expression); - this.state = 368; + this.state = 387; if (!(this.precpred(this._ctx, 9))) { throw this.createFailedPredicateException("this.precpred(this._ctx, 9)"); } - this.state = 369; - (localctx as BinaryOpContext)._op = this.match(CashScriptParser.T__52); - this.state = 370; + this.state = 388; + (localctx as BinaryOpContext)._op = this.match(CashScriptParser.T__53); + this.state = 389; (localctx as BinaryOpContext)._right = this.expression(10); } break; @@ -1949,13 +2040,13 @@ export default class CashScriptParser extends Parser { localctx = new BinaryOpContext(this, new ExpressionContext(this, _parentctx, _parentState)); (localctx as BinaryOpContext)._left = _prevctx; this.pushNewRecursionContext(localctx, _startState, CashScriptParser.RULE_expression); - this.state = 371; + this.state = 390; if (!(this.precpred(this._ctx, 8))) { throw this.createFailedPredicateException("this.precpred(this._ctx, 8)"); } - this.state = 372; + this.state = 391; (localctx as BinaryOpContext)._op = this.match(CashScriptParser.T__3); - this.state = 373; + this.state = 392; (localctx as BinaryOpContext)._right = this.expression(9); } break; @@ -1964,13 +2055,13 @@ export default class CashScriptParser extends Parser { localctx = new BinaryOpContext(this, new ExpressionContext(this, _parentctx, _parentState)); (localctx as BinaryOpContext)._left = _prevctx; this.pushNewRecursionContext(localctx, _startState, CashScriptParser.RULE_expression); - this.state = 374; + this.state = 393; if (!(this.precpred(this._ctx, 7))) { throw this.createFailedPredicateException("this.precpred(this._ctx, 7)"); } - this.state = 375; - (localctx as BinaryOpContext)._op = this.match(CashScriptParser.T__53); - this.state = 376; + this.state = 394; + (localctx as BinaryOpContext)._op = this.match(CashScriptParser.T__54); + this.state = 395; (localctx as BinaryOpContext)._right = this.expression(8); } break; @@ -1979,13 +2070,13 @@ export default class CashScriptParser extends Parser { localctx = new BinaryOpContext(this, new ExpressionContext(this, _parentctx, _parentState)); (localctx as BinaryOpContext)._left = _prevctx; this.pushNewRecursionContext(localctx, _startState, CashScriptParser.RULE_expression); - this.state = 377; + this.state = 396; if (!(this.precpred(this._ctx, 6))) { throw this.createFailedPredicateException("this.precpred(this._ctx, 6)"); } - this.state = 378; - (localctx as BinaryOpContext)._op = this.match(CashScriptParser.T__54); - this.state = 379; + this.state = 397; + (localctx as BinaryOpContext)._op = this.match(CashScriptParser.T__55); + this.state = 398; (localctx as BinaryOpContext)._right = this.expression(7); } break; @@ -1994,13 +2085,13 @@ export default class CashScriptParser extends Parser { localctx = new BinaryOpContext(this, new ExpressionContext(this, _parentctx, _parentState)); (localctx as BinaryOpContext)._left = _prevctx; this.pushNewRecursionContext(localctx, _startState, CashScriptParser.RULE_expression); - this.state = 380; + this.state = 399; if (!(this.precpred(this._ctx, 5))) { throw this.createFailedPredicateException("this.precpred(this._ctx, 5)"); } - this.state = 381; - (localctx as BinaryOpContext)._op = this.match(CashScriptParser.T__55); - this.state = 382; + this.state = 400; + (localctx as BinaryOpContext)._op = this.match(CashScriptParser.T__56); + this.state = 401; (localctx as BinaryOpContext)._right = this.expression(6); } break; @@ -2008,30 +2099,30 @@ export default class CashScriptParser extends Parser { { localctx = new TupleIndexOpContext(this, new ExpressionContext(this, _parentctx, _parentState)); this.pushNewRecursionContext(localctx, _startState, CashScriptParser.RULE_expression); - this.state = 383; + this.state = 402; if (!(this.precpred(this._ctx, 21))) { throw this.createFailedPredicateException("this.precpred(this._ctx, 21)"); } - this.state = 384; - this.match(CashScriptParser.T__25); - this.state = 385; - (localctx as TupleIndexOpContext)._index = this.match(CashScriptParser.NumberLiteral); - this.state = 386; + this.state = 403; this.match(CashScriptParser.T__26); + this.state = 404; + (localctx as TupleIndexOpContext)._index = this.match(CashScriptParser.NumberLiteral); + this.state = 405; + this.match(CashScriptParser.T__27); } break; case 12: { localctx = new UnaryOpContext(this, new ExpressionContext(this, _parentctx, _parentState)); this.pushNewRecursionContext(localctx, _startState, CashScriptParser.RULE_expression); - this.state = 387; + this.state = 406; if (!(this.precpred(this._ctx, 18))) { throw this.createFailedPredicateException("this.precpred(this._ctx, 18)"); } - this.state = 388; + this.state = 407; (localctx as UnaryOpContext)._op = this._input.LT(1); _la = this._input.LA(1); - if(!(_la===39 || _la===40)) { + if(!(_la===40 || _la===41)) { (localctx as UnaryOpContext)._op = this._errHandler.recoverInline(this); } else { @@ -2045,18 +2136,18 @@ export default class CashScriptParser extends Parser { localctx = new BinaryOpContext(this, new ExpressionContext(this, _parentctx, _parentState)); (localctx as BinaryOpContext)._left = _prevctx; this.pushNewRecursionContext(localctx, _startState, CashScriptParser.RULE_expression); - this.state = 389; + this.state = 408; if (!(this.precpred(this._ctx, 17))) { throw this.createFailedPredicateException("this.precpred(this._ctx, 17)"); } - this.state = 390; - (localctx as BinaryOpContext)._op = this.match(CashScriptParser.T__40); - this.state = 391; - this.match(CashScriptParser.T__14); - this.state = 392; + this.state = 409; + (localctx as BinaryOpContext)._op = this.match(CashScriptParser.T__41); + this.state = 410; + this.match(CashScriptParser.T__15); + this.state = 411; (localctx as BinaryOpContext)._right = this.expression(0); - this.state = 393; - this.match(CashScriptParser.T__16); + this.state = 412; + this.match(CashScriptParser.T__17); } break; case 14: @@ -2064,30 +2155,30 @@ export default class CashScriptParser extends Parser { localctx = new SliceContext(this, new ExpressionContext(this, _parentctx, _parentState)); (localctx as SliceContext)._element = _prevctx; this.pushNewRecursionContext(localctx, _startState, CashScriptParser.RULE_expression); - this.state = 395; + this.state = 414; if (!(this.precpred(this._ctx, 16))) { throw this.createFailedPredicateException("this.precpred(this._ctx, 16)"); } - this.state = 396; - this.match(CashScriptParser.T__41); - this.state = 397; - this.match(CashScriptParser.T__14); - this.state = 398; - (localctx as SliceContext)._start = this.expression(0); - this.state = 399; + this.state = 415; + this.match(CashScriptParser.T__42); + this.state = 416; this.match(CashScriptParser.T__15); - this.state = 400; - (localctx as SliceContext)._end = this.expression(0); - this.state = 401; + this.state = 417; + (localctx as SliceContext)._start = this.expression(0); + this.state = 418; this.match(CashScriptParser.T__16); + this.state = 419; + (localctx as SliceContext)._end = this.expression(0); + this.state = 420; + this.match(CashScriptParser.T__17); } break; } } } - this.state = 407; + this.state = 426; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 32, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 34, this._ctx); } } } @@ -2108,12 +2199,12 @@ export default class CashScriptParser extends Parser { // @RuleVersion(0) public modifier(): ModifierContext { let localctx: ModifierContext = new ModifierContext(this, this._ctx, this.state); - this.enterRule(localctx, 64, CashScriptParser.RULE_modifier); + this.enterRule(localctx, 68, CashScriptParser.RULE_modifier); try { this.enterOuterAlt(localctx, 1); { - this.state = 408; - this.match(CashScriptParser.T__56); + this.state = 427; + this.match(CashScriptParser.T__57); } } catch (re) { @@ -2133,43 +2224,43 @@ export default class CashScriptParser extends Parser { // @RuleVersion(0) public literal(): LiteralContext { let localctx: LiteralContext = new LiteralContext(this, this._ctx, this.state); - this.enterRule(localctx, 66, CashScriptParser.RULE_literal); + this.enterRule(localctx, 70, CashScriptParser.RULE_literal); try { - this.state = 415; + this.state = 434; this._errHandler.sync(this); switch (this._input.LA(1)) { - case 59: + case 60: this.enterOuterAlt(localctx, 1); { - this.state = 410; + this.state = 429; this.match(CashScriptParser.BooleanLiteral); } break; - case 61: + case 62: this.enterOuterAlt(localctx, 2); { - this.state = 411; + this.state = 430; this.numberLiteral(); } break; - case 68: + case 69: this.enterOuterAlt(localctx, 3); { - this.state = 412; + this.state = 431; this.match(CashScriptParser.StringLiteral); } break; - case 69: + case 70: this.enterOuterAlt(localctx, 4); { - this.state = 413; + this.state = 432; this.match(CashScriptParser.DateLiteral); } break; - case 70: + case 71: this.enterOuterAlt(localctx, 5); { - this.state = 414; + this.state = 433; this.match(CashScriptParser.HexLiteral); } break; @@ -2194,18 +2285,18 @@ export default class CashScriptParser extends Parser { // @RuleVersion(0) public numberLiteral(): NumberLiteralContext { let localctx: NumberLiteralContext = new NumberLiteralContext(this, this._ctx, this.state); - this.enterRule(localctx, 68, CashScriptParser.RULE_numberLiteral); + this.enterRule(localctx, 72, CashScriptParser.RULE_numberLiteral); try { this.enterOuterAlt(localctx, 1); { - this.state = 417; + this.state = 436; this.match(CashScriptParser.NumberLiteral); - this.state = 419; + this.state = 438; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 34, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 36, this._ctx) ) { case 1: { - this.state = 418; + this.state = 437; this.match(CashScriptParser.NumberUnit); } break; @@ -2229,14 +2320,14 @@ export default class CashScriptParser extends Parser { // @RuleVersion(0) public typeName(): TypeNameContext { let localctx: TypeNameContext = new TypeNameContext(this, this._ctx, this.state); - this.enterRule(localctx, 70, CashScriptParser.RULE_typeName); + this.enterRule(localctx, 74, CashScriptParser.RULE_typeName); let _la: number; try { this.enterOuterAlt(localctx, 1); { - this.state = 421; + this.state = 440; _la = this._input.LA(1); - if(!(((((_la - 64)) & ~0x1F) === 0 && ((1 << (_la - 64)) & 7) !== 0))) { + if(!(((((_la - 65)) & ~0x1F) === 0 && ((1 << (_la - 65)) & 7) !== 0))) { this._errHandler.recoverInline(this); } else { @@ -2262,14 +2353,14 @@ export default class CashScriptParser extends Parser { // @RuleVersion(0) public typeCast(): TypeCastContext { let localctx: TypeCastContext = new TypeCastContext(this, this._ctx, this.state); - this.enterRule(localctx, 72, CashScriptParser.RULE_typeCast); + this.enterRule(localctx, 76, CashScriptParser.RULE_typeCast); let _la: number; try { this.enterOuterAlt(localctx, 1); { - this.state = 423; + this.state = 442; _la = this._input.LA(1); - if(!(((((_la - 64)) & ~0x1F) === 0 && ((1 << (_la - 64)) & 259) !== 0))) { + if(!(((((_la - 65)) & ~0x1F) === 0 && ((1 << (_la - 65)) & 259) !== 0))) { this._errHandler.recoverInline(this); } else { @@ -2295,7 +2386,7 @@ export default class CashScriptParser extends Parser { public sempred(localctx: RuleContext, ruleIndex: number, predIndex: number): boolean { switch (ruleIndex) { - case 31: + case 33: return this.expression_sempred(localctx as ExpressionContext, predIndex); } return true; @@ -2334,146 +2425,153 @@ export default class CashScriptParser extends Parser { return true; } - public static readonly _serializedATN: number[] = [4,1,77,426,2,0,7,0,2, + public static readonly _serializedATN: number[] = [4,1,78,445,2,0,7,0,2, 1,7,1,2,2,7,2,2,3,7,3,2,4,7,4,2,5,7,5,2,6,7,6,2,7,7,7,2,8,7,8,2,9,7,9,2, 10,7,10,2,11,7,11,2,12,7,12,2,13,7,13,2,14,7,14,2,15,7,15,2,16,7,16,2,17, 7,17,2,18,7,18,2,19,7,19,2,20,7,20,2,21,7,21,2,22,7,22,2,23,7,23,2,24,7, 24,2,25,7,25,2,26,7,26,2,27,7,27,2,28,7,28,2,29,7,29,2,30,7,30,2,31,7,31, - 2,32,7,32,2,33,7,33,2,34,7,34,2,35,7,35,2,36,7,36,1,0,5,0,76,8,0,10,0,12, - 0,79,9,0,1,0,1,0,1,0,1,1,1,1,1,1,1,1,1,1,1,2,1,2,1,3,1,3,3,3,93,8,3,1,4, - 3,4,96,8,4,1,4,1,4,1,5,1,5,1,6,1,6,1,6,1,6,1,6,5,6,107,8,6,10,6,12,6,110, - 9,6,1,6,1,6,1,7,1,7,1,7,1,7,1,7,5,7,119,8,7,10,7,12,7,122,9,7,1,7,1,7,1, - 8,1,8,1,8,1,8,5,8,130,8,8,10,8,12,8,133,9,8,1,8,3,8,136,8,8,3,8,138,8,8, - 1,8,1,8,1,9,1,9,1,9,1,10,1,10,5,10,147,8,10,10,10,12,10,150,9,10,1,10,1, - 10,3,10,154,8,10,1,11,1,11,1,11,1,11,3,11,160,8,11,1,12,1,12,1,12,1,12, - 1,12,1,12,3,12,168,8,12,1,13,1,13,3,13,172,8,13,1,14,1,14,5,14,176,8,14, - 10,14,12,14,179,9,14,1,14,1,14,1,14,1,14,1,15,1,15,1,15,1,15,1,15,1,15, - 1,15,1,15,1,16,1,16,1,16,1,16,1,17,1,17,1,17,1,17,1,17,1,17,1,17,3,17,204, - 8,17,1,17,1,17,1,18,1,18,1,18,1,18,1,18,3,18,213,8,18,1,18,1,18,1,19,1, - 19,1,19,1,20,1,20,1,20,1,20,1,20,1,20,1,20,3,20,227,8,20,1,21,1,21,1,21, - 3,21,232,8,21,1,22,1,22,1,22,1,22,1,22,1,22,1,22,1,22,1,23,1,23,1,23,1, - 23,1,23,1,23,1,24,1,24,1,24,1,24,1,24,1,24,1,24,1,24,1,24,1,24,1,25,1,25, - 3,25,260,8,25,1,26,1,26,1,27,1,27,3,27,266,8,27,1,28,1,28,1,28,1,28,5,28, - 272,8,28,10,28,12,28,275,9,28,1,28,3,28,278,8,28,3,28,280,8,28,1,28,1,28, - 1,29,1,29,1,29,1,30,1,30,1,30,1,30,5,30,291,8,30,10,30,12,30,294,9,30,1, - 30,3,30,297,8,30,3,30,299,8,30,1,30,1,30,1,31,1,31,1,31,1,31,1,31,1,31, - 1,31,1,31,1,31,3,31,312,8,31,1,31,1,31,1,31,1,31,1,31,1,31,1,31,1,31,1, - 31,1,31,1,31,1,31,1,31,1,31,1,31,1,31,1,31,1,31,1,31,1,31,1,31,1,31,1,31, - 1,31,5,31,338,8,31,10,31,12,31,341,9,31,1,31,3,31,344,8,31,3,31,346,8,31, - 1,31,1,31,1,31,1,31,3,31,352,8,31,1,31,1,31,1,31,1,31,1,31,1,31,1,31,1, - 31,1,31,1,31,1,31,1,31,1,31,1,31,1,31,1,31,1,31,1,31,1,31,1,31,1,31,1,31, - 1,31,1,31,1,31,1,31,1,31,1,31,1,31,1,31,1,31,1,31,1,31,1,31,1,31,1,31,1, - 31,1,31,1,31,1,31,1,31,1,31,1,31,1,31,1,31,1,31,1,31,1,31,1,31,1,31,5,31, - 404,8,31,10,31,12,31,407,9,31,1,32,1,32,1,33,1,33,1,33,1,33,1,33,3,33,416, - 8,33,1,34,1,34,3,34,420,8,34,1,35,1,35,1,36,1,36,1,36,0,1,62,37,0,2,4,6, - 8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54, - 56,58,60,62,64,66,68,70,72,0,12,1,0,4,10,1,0,29,33,2,0,29,33,35,38,2,0, - 5,5,43,44,1,0,45,47,2,0,44,44,48,48,1,0,49,50,1,0,6,9,1,0,51,52,1,0,39, - 40,1,0,64,66,2,0,64,65,72,72,452,0,77,1,0,0,0,2,83,1,0,0,0,4,88,1,0,0,0, - 6,90,1,0,0,0,8,95,1,0,0,0,10,99,1,0,0,0,12,101,1,0,0,0,14,113,1,0,0,0,16, - 125,1,0,0,0,18,141,1,0,0,0,20,153,1,0,0,0,22,159,1,0,0,0,24,167,1,0,0,0, - 26,171,1,0,0,0,28,173,1,0,0,0,30,184,1,0,0,0,32,192,1,0,0,0,34,196,1,0, - 0,0,36,207,1,0,0,0,38,216,1,0,0,0,40,219,1,0,0,0,42,231,1,0,0,0,44,233, - 1,0,0,0,46,241,1,0,0,0,48,247,1,0,0,0,50,259,1,0,0,0,52,261,1,0,0,0,54, - 265,1,0,0,0,56,267,1,0,0,0,58,283,1,0,0,0,60,286,1,0,0,0,62,351,1,0,0,0, - 64,408,1,0,0,0,66,415,1,0,0,0,68,417,1,0,0,0,70,421,1,0,0,0,72,423,1,0, - 0,0,74,76,3,2,1,0,75,74,1,0,0,0,76,79,1,0,0,0,77,75,1,0,0,0,77,78,1,0,0, - 0,78,80,1,0,0,0,79,77,1,0,0,0,80,81,3,12,6,0,81,82,5,0,0,1,82,1,1,0,0,0, - 83,84,5,1,0,0,84,85,3,4,2,0,85,86,3,6,3,0,86,87,5,2,0,0,87,3,1,0,0,0,88, - 89,5,3,0,0,89,5,1,0,0,0,90,92,3,8,4,0,91,93,3,8,4,0,92,91,1,0,0,0,92,93, - 1,0,0,0,93,7,1,0,0,0,94,96,3,10,5,0,95,94,1,0,0,0,95,96,1,0,0,0,96,97,1, - 0,0,0,97,98,5,58,0,0,98,9,1,0,0,0,99,100,7,0,0,0,100,11,1,0,0,0,101,102, - 5,11,0,0,102,103,5,74,0,0,103,104,3,16,8,0,104,108,5,12,0,0,105,107,3,14, - 7,0,106,105,1,0,0,0,107,110,1,0,0,0,108,106,1,0,0,0,108,109,1,0,0,0,109, - 111,1,0,0,0,110,108,1,0,0,0,111,112,5,13,0,0,112,13,1,0,0,0,113,114,5,14, - 0,0,114,115,5,74,0,0,115,116,3,16,8,0,116,120,5,12,0,0,117,119,3,22,11, - 0,118,117,1,0,0,0,119,122,1,0,0,0,120,118,1,0,0,0,120,121,1,0,0,0,121,123, - 1,0,0,0,122,120,1,0,0,0,123,124,5,13,0,0,124,15,1,0,0,0,125,137,5,15,0, - 0,126,131,3,18,9,0,127,128,5,16,0,0,128,130,3,18,9,0,129,127,1,0,0,0,130, - 133,1,0,0,0,131,129,1,0,0,0,131,132,1,0,0,0,132,135,1,0,0,0,133,131,1,0, - 0,0,134,136,5,16,0,0,135,134,1,0,0,0,135,136,1,0,0,0,136,138,1,0,0,0,137, - 126,1,0,0,0,137,138,1,0,0,0,138,139,1,0,0,0,139,140,5,17,0,0,140,17,1,0, - 0,0,141,142,3,70,35,0,142,143,5,74,0,0,143,19,1,0,0,0,144,148,5,12,0,0, - 145,147,3,22,11,0,146,145,1,0,0,0,147,150,1,0,0,0,148,146,1,0,0,0,148,149, - 1,0,0,0,149,151,1,0,0,0,150,148,1,0,0,0,151,154,5,13,0,0,152,154,3,22,11, - 0,153,144,1,0,0,0,153,152,1,0,0,0,154,21,1,0,0,0,155,160,3,26,13,0,156, - 157,3,24,12,0,157,158,5,2,0,0,158,160,1,0,0,0,159,155,1,0,0,0,159,156,1, - 0,0,0,160,23,1,0,0,0,161,168,3,28,14,0,162,168,3,30,15,0,163,168,3,32,16, - 0,164,168,3,34,17,0,165,168,3,36,18,0,166,168,3,38,19,0,167,161,1,0,0,0, - 167,162,1,0,0,0,167,163,1,0,0,0,167,164,1,0,0,0,167,165,1,0,0,0,167,166, - 1,0,0,0,168,25,1,0,0,0,169,172,3,40,20,0,170,172,3,42,21,0,171,169,1,0, - 0,0,171,170,1,0,0,0,172,27,1,0,0,0,173,177,3,70,35,0,174,176,3,64,32,0, - 175,174,1,0,0,0,176,179,1,0,0,0,177,175,1,0,0,0,177,178,1,0,0,0,178,180, - 1,0,0,0,179,177,1,0,0,0,180,181,5,74,0,0,181,182,5,10,0,0,182,183,3,62, - 31,0,183,29,1,0,0,0,184,185,3,70,35,0,185,186,5,74,0,0,186,187,5,16,0,0, - 187,188,3,70,35,0,188,189,5,74,0,0,189,190,5,10,0,0,190,191,3,62,31,0,191, - 31,1,0,0,0,192,193,5,74,0,0,193,194,5,10,0,0,194,195,3,62,31,0,195,33,1, - 0,0,0,196,197,5,18,0,0,197,198,5,15,0,0,198,199,5,71,0,0,199,200,5,6,0, - 0,200,203,3,62,31,0,201,202,5,16,0,0,202,204,3,52,26,0,203,201,1,0,0,0, - 203,204,1,0,0,0,204,205,1,0,0,0,205,206,5,17,0,0,206,35,1,0,0,0,207,208, - 5,18,0,0,208,209,5,15,0,0,209,212,3,62,31,0,210,211,5,16,0,0,211,213,3, - 52,26,0,212,210,1,0,0,0,212,213,1,0,0,0,213,214,1,0,0,0,214,215,5,17,0, - 0,215,37,1,0,0,0,216,217,5,19,0,0,217,218,3,56,28,0,218,39,1,0,0,0,219, - 220,5,20,0,0,220,221,5,15,0,0,221,222,3,62,31,0,222,223,5,17,0,0,223,226, - 3,20,10,0,224,225,5,21,0,0,225,227,3,20,10,0,226,224,1,0,0,0,226,227,1, - 0,0,0,227,41,1,0,0,0,228,232,3,44,22,0,229,232,3,46,23,0,230,232,3,48,24, - 0,231,228,1,0,0,0,231,229,1,0,0,0,231,230,1,0,0,0,232,43,1,0,0,0,233,234, - 5,22,0,0,234,235,3,20,10,0,235,236,5,23,0,0,236,237,5,15,0,0,237,238,3, - 62,31,0,238,239,5,17,0,0,239,240,5,2,0,0,240,45,1,0,0,0,241,242,5,23,0, - 0,242,243,5,15,0,0,243,244,3,62,31,0,244,245,5,17,0,0,245,246,3,20,10,0, - 246,47,1,0,0,0,247,248,5,24,0,0,248,249,5,15,0,0,249,250,3,50,25,0,250, - 251,5,2,0,0,251,252,3,62,31,0,252,253,5,2,0,0,253,254,3,32,16,0,254,255, - 5,17,0,0,255,256,3,20,10,0,256,49,1,0,0,0,257,260,3,28,14,0,258,260,3,32, - 16,0,259,257,1,0,0,0,259,258,1,0,0,0,260,51,1,0,0,0,261,262,5,68,0,0,262, - 53,1,0,0,0,263,266,5,74,0,0,264,266,3,66,33,0,265,263,1,0,0,0,265,264,1, - 0,0,0,266,55,1,0,0,0,267,279,5,15,0,0,268,273,3,54,27,0,269,270,5,16,0, - 0,270,272,3,54,27,0,271,269,1,0,0,0,272,275,1,0,0,0,273,271,1,0,0,0,273, - 274,1,0,0,0,274,277,1,0,0,0,275,273,1,0,0,0,276,278,5,16,0,0,277,276,1, - 0,0,0,277,278,1,0,0,0,278,280,1,0,0,0,279,268,1,0,0,0,279,280,1,0,0,0,280, - 281,1,0,0,0,281,282,5,17,0,0,282,57,1,0,0,0,283,284,5,74,0,0,284,285,3, - 60,30,0,285,59,1,0,0,0,286,298,5,15,0,0,287,292,3,62,31,0,288,289,5,16, - 0,0,289,291,3,62,31,0,290,288,1,0,0,0,291,294,1,0,0,0,292,290,1,0,0,0,292, - 293,1,0,0,0,293,296,1,0,0,0,294,292,1,0,0,0,295,297,5,16,0,0,296,295,1, + 2,32,7,32,2,33,7,33,2,34,7,34,2,35,7,35,2,36,7,36,2,37,7,37,2,38,7,38,1, + 0,5,0,80,8,0,10,0,12,0,83,9,0,1,0,1,0,1,0,1,1,1,1,3,1,90,8,1,1,2,1,2,1, + 2,1,2,1,2,1,3,1,3,1,4,1,4,3,4,101,8,4,1,5,3,5,104,8,5,1,5,1,5,1,6,1,6,1, + 7,1,7,1,7,1,7,1,7,5,7,115,8,7,10,7,12,7,118,9,7,1,7,1,7,1,8,1,8,1,8,1,8, + 5,8,126,8,8,10,8,12,8,129,9,8,1,8,1,8,1,9,1,9,1,9,1,9,1,9,5,9,138,8,9,10, + 9,12,9,141,9,9,1,9,1,9,1,10,1,10,1,10,1,10,5,10,149,8,10,10,10,12,10,152, + 9,10,1,10,3,10,155,8,10,3,10,157,8,10,1,10,1,10,1,11,1,11,1,11,1,12,1,12, + 5,12,166,8,12,10,12,12,12,169,9,12,1,12,1,12,3,12,173,8,12,1,13,1,13,1, + 13,1,13,3,13,179,8,13,1,14,1,14,1,14,1,14,1,14,1,14,3,14,187,8,14,1,15, + 1,15,3,15,191,8,15,1,16,1,16,5,16,195,8,16,10,16,12,16,198,9,16,1,16,1, + 16,1,16,1,16,1,17,1,17,1,17,1,17,1,17,1,17,1,17,1,17,1,18,1,18,1,18,1,18, + 1,19,1,19,1,19,1,19,1,19,1,19,1,19,3,19,223,8,19,1,19,1,19,1,20,1,20,1, + 20,1,20,1,20,3,20,232,8,20,1,20,1,20,1,21,1,21,1,21,1,22,1,22,1,22,1,22, + 1,22,1,22,1,22,3,22,246,8,22,1,23,1,23,1,23,3,23,251,8,23,1,24,1,24,1,24, + 1,24,1,24,1,24,1,24,1,24,1,25,1,25,1,25,1,25,1,25,1,25,1,26,1,26,1,26,1, + 26,1,26,1,26,1,26,1,26,1,26,1,26,1,27,1,27,3,27,279,8,27,1,28,1,28,1,29, + 1,29,3,29,285,8,29,1,30,1,30,1,30,1,30,5,30,291,8,30,10,30,12,30,294,9, + 30,1,30,3,30,297,8,30,3,30,299,8,30,1,30,1,30,1,31,1,31,1,31,1,32,1,32, + 1,32,1,32,5,32,310,8,32,10,32,12,32,313,9,32,1,32,3,32,316,8,32,3,32,318, + 8,32,1,32,1,32,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,3,33,331,8, + 33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33, + 1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,5,33,357,8,33,10,33,12, + 33,360,9,33,1,33,3,33,363,8,33,3,33,365,8,33,1,33,1,33,1,33,1,33,3,33,371, + 8,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1, + 33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33, + 1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1, + 33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,5,33,423,8,33,10,33,12,33,426,9,33, + 1,34,1,34,1,35,1,35,1,35,1,35,1,35,3,35,435,8,35,1,36,1,36,3,36,439,8,36, + 1,37,1,37,1,38,1,38,1,38,0,1,66,39,0,2,4,6,8,10,12,14,16,18,20,22,24,26, + 28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62,64,66,68,70,72,74, + 76,0,12,1,0,4,10,1,0,30,34,2,0,30,34,36,39,2,0,5,5,44,45,1,0,46,48,2,0, + 45,45,49,49,1,0,50,51,1,0,6,9,1,0,52,53,1,0,40,41,1,0,65,67,2,0,65,66,73, + 73,471,0,81,1,0,0,0,2,89,1,0,0,0,4,91,1,0,0,0,6,96,1,0,0,0,8,98,1,0,0,0, + 10,103,1,0,0,0,12,107,1,0,0,0,14,109,1,0,0,0,16,121,1,0,0,0,18,132,1,0, + 0,0,20,144,1,0,0,0,22,160,1,0,0,0,24,172,1,0,0,0,26,178,1,0,0,0,28,186, + 1,0,0,0,30,190,1,0,0,0,32,192,1,0,0,0,34,203,1,0,0,0,36,211,1,0,0,0,38, + 215,1,0,0,0,40,226,1,0,0,0,42,235,1,0,0,0,44,238,1,0,0,0,46,250,1,0,0,0, + 48,252,1,0,0,0,50,260,1,0,0,0,52,266,1,0,0,0,54,278,1,0,0,0,56,280,1,0, + 0,0,58,284,1,0,0,0,60,286,1,0,0,0,62,302,1,0,0,0,64,305,1,0,0,0,66,370, + 1,0,0,0,68,427,1,0,0,0,70,434,1,0,0,0,72,436,1,0,0,0,74,440,1,0,0,0,76, + 442,1,0,0,0,78,80,3,4,2,0,79,78,1,0,0,0,80,83,1,0,0,0,81,79,1,0,0,0,81, + 82,1,0,0,0,82,84,1,0,0,0,83,81,1,0,0,0,84,85,3,2,1,0,85,86,5,0,0,1,86,1, + 1,0,0,0,87,90,3,14,7,0,88,90,3,16,8,0,89,87,1,0,0,0,89,88,1,0,0,0,90,3, + 1,0,0,0,91,92,5,1,0,0,92,93,3,6,3,0,93,94,3,8,4,0,94,95,5,2,0,0,95,5,1, + 0,0,0,96,97,5,3,0,0,97,7,1,0,0,0,98,100,3,10,5,0,99,101,3,10,5,0,100,99, + 1,0,0,0,100,101,1,0,0,0,101,9,1,0,0,0,102,104,3,12,6,0,103,102,1,0,0,0, + 103,104,1,0,0,0,104,105,1,0,0,0,105,106,5,59,0,0,106,11,1,0,0,0,107,108, + 7,0,0,0,108,13,1,0,0,0,109,110,5,11,0,0,110,111,5,75,0,0,111,112,3,20,10, + 0,112,116,5,12,0,0,113,115,3,18,9,0,114,113,1,0,0,0,115,118,1,0,0,0,116, + 114,1,0,0,0,116,117,1,0,0,0,117,119,1,0,0,0,118,116,1,0,0,0,119,120,5,13, + 0,0,120,15,1,0,0,0,121,122,5,14,0,0,122,123,5,75,0,0,123,127,5,12,0,0,124, + 126,3,18,9,0,125,124,1,0,0,0,126,129,1,0,0,0,127,125,1,0,0,0,127,128,1, + 0,0,0,128,130,1,0,0,0,129,127,1,0,0,0,130,131,5,13,0,0,131,17,1,0,0,0,132, + 133,5,15,0,0,133,134,5,75,0,0,134,135,3,20,10,0,135,139,5,12,0,0,136,138, + 3,26,13,0,137,136,1,0,0,0,138,141,1,0,0,0,139,137,1,0,0,0,139,140,1,0,0, + 0,140,142,1,0,0,0,141,139,1,0,0,0,142,143,5,13,0,0,143,19,1,0,0,0,144,156, + 5,16,0,0,145,150,3,22,11,0,146,147,5,17,0,0,147,149,3,22,11,0,148,146,1, + 0,0,0,149,152,1,0,0,0,150,148,1,0,0,0,150,151,1,0,0,0,151,154,1,0,0,0,152, + 150,1,0,0,0,153,155,5,17,0,0,154,153,1,0,0,0,154,155,1,0,0,0,155,157,1, + 0,0,0,156,145,1,0,0,0,156,157,1,0,0,0,157,158,1,0,0,0,158,159,5,18,0,0, + 159,21,1,0,0,0,160,161,3,74,37,0,161,162,5,75,0,0,162,23,1,0,0,0,163,167, + 5,12,0,0,164,166,3,26,13,0,165,164,1,0,0,0,166,169,1,0,0,0,167,165,1,0, + 0,0,167,168,1,0,0,0,168,170,1,0,0,0,169,167,1,0,0,0,170,173,5,13,0,0,171, + 173,3,26,13,0,172,163,1,0,0,0,172,171,1,0,0,0,173,25,1,0,0,0,174,179,3, + 30,15,0,175,176,3,28,14,0,176,177,5,2,0,0,177,179,1,0,0,0,178,174,1,0,0, + 0,178,175,1,0,0,0,179,27,1,0,0,0,180,187,3,32,16,0,181,187,3,34,17,0,182, + 187,3,36,18,0,183,187,3,38,19,0,184,187,3,40,20,0,185,187,3,42,21,0,186, + 180,1,0,0,0,186,181,1,0,0,0,186,182,1,0,0,0,186,183,1,0,0,0,186,184,1,0, + 0,0,186,185,1,0,0,0,187,29,1,0,0,0,188,191,3,44,22,0,189,191,3,46,23,0, + 190,188,1,0,0,0,190,189,1,0,0,0,191,31,1,0,0,0,192,196,3,74,37,0,193,195, + 3,68,34,0,194,193,1,0,0,0,195,198,1,0,0,0,196,194,1,0,0,0,196,197,1,0,0, + 0,197,199,1,0,0,0,198,196,1,0,0,0,199,200,5,75,0,0,200,201,5,10,0,0,201, + 202,3,66,33,0,202,33,1,0,0,0,203,204,3,74,37,0,204,205,5,75,0,0,205,206, + 5,17,0,0,206,207,3,74,37,0,207,208,5,75,0,0,208,209,5,10,0,0,209,210,3, + 66,33,0,210,35,1,0,0,0,211,212,5,75,0,0,212,213,5,10,0,0,213,214,3,66,33, + 0,214,37,1,0,0,0,215,216,5,19,0,0,216,217,5,16,0,0,217,218,5,72,0,0,218, + 219,5,6,0,0,219,222,3,66,33,0,220,221,5,17,0,0,221,223,3,56,28,0,222,220, + 1,0,0,0,222,223,1,0,0,0,223,224,1,0,0,0,224,225,5,18,0,0,225,39,1,0,0,0, + 226,227,5,19,0,0,227,228,5,16,0,0,228,231,3,66,33,0,229,230,5,17,0,0,230, + 232,3,56,28,0,231,229,1,0,0,0,231,232,1,0,0,0,232,233,1,0,0,0,233,234,5, + 18,0,0,234,41,1,0,0,0,235,236,5,20,0,0,236,237,3,60,30,0,237,43,1,0,0,0, + 238,239,5,21,0,0,239,240,5,16,0,0,240,241,3,66,33,0,241,242,5,18,0,0,242, + 245,3,24,12,0,243,244,5,22,0,0,244,246,3,24,12,0,245,243,1,0,0,0,245,246, + 1,0,0,0,246,45,1,0,0,0,247,251,3,48,24,0,248,251,3,50,25,0,249,251,3,52, + 26,0,250,247,1,0,0,0,250,248,1,0,0,0,250,249,1,0,0,0,251,47,1,0,0,0,252, + 253,5,23,0,0,253,254,3,24,12,0,254,255,5,24,0,0,255,256,5,16,0,0,256,257, + 3,66,33,0,257,258,5,18,0,0,258,259,5,2,0,0,259,49,1,0,0,0,260,261,5,24, + 0,0,261,262,5,16,0,0,262,263,3,66,33,0,263,264,5,18,0,0,264,265,3,24,12, + 0,265,51,1,0,0,0,266,267,5,25,0,0,267,268,5,16,0,0,268,269,3,54,27,0,269, + 270,5,2,0,0,270,271,3,66,33,0,271,272,5,2,0,0,272,273,3,36,18,0,273,274, + 5,18,0,0,274,275,3,24,12,0,275,53,1,0,0,0,276,279,3,32,16,0,277,279,3,36, + 18,0,278,276,1,0,0,0,278,277,1,0,0,0,279,55,1,0,0,0,280,281,5,69,0,0,281, + 57,1,0,0,0,282,285,5,75,0,0,283,285,3,70,35,0,284,282,1,0,0,0,284,283,1, + 0,0,0,285,59,1,0,0,0,286,298,5,16,0,0,287,292,3,58,29,0,288,289,5,17,0, + 0,289,291,3,58,29,0,290,288,1,0,0,0,291,294,1,0,0,0,292,290,1,0,0,0,292, + 293,1,0,0,0,293,296,1,0,0,0,294,292,1,0,0,0,295,297,5,17,0,0,296,295,1, 0,0,0,296,297,1,0,0,0,297,299,1,0,0,0,298,287,1,0,0,0,298,299,1,0,0,0,299, - 300,1,0,0,0,300,301,5,17,0,0,301,61,1,0,0,0,302,303,6,31,-1,0,303,304,5, - 15,0,0,304,305,3,62,31,0,305,306,5,17,0,0,306,352,1,0,0,0,307,308,3,72, - 36,0,308,309,5,15,0,0,309,311,3,62,31,0,310,312,5,16,0,0,311,310,1,0,0, - 0,311,312,1,0,0,0,312,313,1,0,0,0,313,314,5,17,0,0,314,352,1,0,0,0,315, - 352,3,58,29,0,316,317,5,25,0,0,317,318,5,74,0,0,318,352,3,60,30,0,319,320, - 5,28,0,0,320,321,5,26,0,0,321,322,3,62,31,0,322,323,5,27,0,0,323,324,7, - 1,0,0,324,352,1,0,0,0,325,326,5,34,0,0,326,327,5,26,0,0,327,328,3,62,31, - 0,328,329,5,27,0,0,329,330,7,2,0,0,330,352,1,0,0,0,331,332,7,3,0,0,332, - 352,3,62,31,15,333,345,5,26,0,0,334,339,3,62,31,0,335,336,5,16,0,0,336, - 338,3,62,31,0,337,335,1,0,0,0,338,341,1,0,0,0,339,337,1,0,0,0,339,340,1, - 0,0,0,340,343,1,0,0,0,341,339,1,0,0,0,342,344,5,16,0,0,343,342,1,0,0,0, - 343,344,1,0,0,0,344,346,1,0,0,0,345,334,1,0,0,0,345,346,1,0,0,0,346,347, - 1,0,0,0,347,352,5,27,0,0,348,352,5,73,0,0,349,352,5,74,0,0,350,352,3,66, - 33,0,351,302,1,0,0,0,351,307,1,0,0,0,351,315,1,0,0,0,351,316,1,0,0,0,351, - 319,1,0,0,0,351,325,1,0,0,0,351,331,1,0,0,0,351,333,1,0,0,0,351,348,1,0, - 0,0,351,349,1,0,0,0,351,350,1,0,0,0,352,405,1,0,0,0,353,354,10,14,0,0,354, - 355,7,4,0,0,355,404,3,62,31,15,356,357,10,13,0,0,357,358,7,5,0,0,358,404, - 3,62,31,14,359,360,10,12,0,0,360,361,7,6,0,0,361,404,3,62,31,13,362,363, - 10,11,0,0,363,364,7,7,0,0,364,404,3,62,31,12,365,366,10,10,0,0,366,367, - 7,8,0,0,367,404,3,62,31,11,368,369,10,9,0,0,369,370,5,53,0,0,370,404,3, - 62,31,10,371,372,10,8,0,0,372,373,5,4,0,0,373,404,3,62,31,9,374,375,10, - 7,0,0,375,376,5,54,0,0,376,404,3,62,31,8,377,378,10,6,0,0,378,379,5,55, - 0,0,379,404,3,62,31,7,380,381,10,5,0,0,381,382,5,56,0,0,382,404,3,62,31, - 6,383,384,10,21,0,0,384,385,5,26,0,0,385,386,5,61,0,0,386,404,5,27,0,0, - 387,388,10,18,0,0,388,404,7,9,0,0,389,390,10,17,0,0,390,391,5,41,0,0,391, - 392,5,15,0,0,392,393,3,62,31,0,393,394,5,17,0,0,394,404,1,0,0,0,395,396, - 10,16,0,0,396,397,5,42,0,0,397,398,5,15,0,0,398,399,3,62,31,0,399,400,5, - 16,0,0,400,401,3,62,31,0,401,402,5,17,0,0,402,404,1,0,0,0,403,353,1,0,0, - 0,403,356,1,0,0,0,403,359,1,0,0,0,403,362,1,0,0,0,403,365,1,0,0,0,403,368, - 1,0,0,0,403,371,1,0,0,0,403,374,1,0,0,0,403,377,1,0,0,0,403,380,1,0,0,0, - 403,383,1,0,0,0,403,387,1,0,0,0,403,389,1,0,0,0,403,395,1,0,0,0,404,407, - 1,0,0,0,405,403,1,0,0,0,405,406,1,0,0,0,406,63,1,0,0,0,407,405,1,0,0,0, - 408,409,5,57,0,0,409,65,1,0,0,0,410,416,5,59,0,0,411,416,3,68,34,0,412, - 416,5,68,0,0,413,416,5,69,0,0,414,416,5,70,0,0,415,410,1,0,0,0,415,411, - 1,0,0,0,415,412,1,0,0,0,415,413,1,0,0,0,415,414,1,0,0,0,416,67,1,0,0,0, - 417,419,5,61,0,0,418,420,5,60,0,0,419,418,1,0,0,0,419,420,1,0,0,0,420,69, - 1,0,0,0,421,422,7,10,0,0,422,71,1,0,0,0,423,424,7,11,0,0,424,73,1,0,0,0, - 35,77,92,95,108,120,131,135,137,148,153,159,167,171,177,203,212,226,231, - 259,265,273,277,279,292,296,298,311,339,343,345,351,403,405,415,419]; + 300,1,0,0,0,300,301,5,18,0,0,301,61,1,0,0,0,302,303,5,75,0,0,303,304,3, + 64,32,0,304,63,1,0,0,0,305,317,5,16,0,0,306,311,3,66,33,0,307,308,5,17, + 0,0,308,310,3,66,33,0,309,307,1,0,0,0,310,313,1,0,0,0,311,309,1,0,0,0,311, + 312,1,0,0,0,312,315,1,0,0,0,313,311,1,0,0,0,314,316,5,17,0,0,315,314,1, + 0,0,0,315,316,1,0,0,0,316,318,1,0,0,0,317,306,1,0,0,0,317,318,1,0,0,0,318, + 319,1,0,0,0,319,320,5,18,0,0,320,65,1,0,0,0,321,322,6,33,-1,0,322,323,5, + 16,0,0,323,324,3,66,33,0,324,325,5,18,0,0,325,371,1,0,0,0,326,327,3,76, + 38,0,327,328,5,16,0,0,328,330,3,66,33,0,329,331,5,17,0,0,330,329,1,0,0, + 0,330,331,1,0,0,0,331,332,1,0,0,0,332,333,5,18,0,0,333,371,1,0,0,0,334, + 371,3,62,31,0,335,336,5,26,0,0,336,337,5,75,0,0,337,371,3,64,32,0,338,339, + 5,29,0,0,339,340,5,27,0,0,340,341,3,66,33,0,341,342,5,28,0,0,342,343,7, + 1,0,0,343,371,1,0,0,0,344,345,5,35,0,0,345,346,5,27,0,0,346,347,3,66,33, + 0,347,348,5,28,0,0,348,349,7,2,0,0,349,371,1,0,0,0,350,351,7,3,0,0,351, + 371,3,66,33,15,352,364,5,27,0,0,353,358,3,66,33,0,354,355,5,17,0,0,355, + 357,3,66,33,0,356,354,1,0,0,0,357,360,1,0,0,0,358,356,1,0,0,0,358,359,1, + 0,0,0,359,362,1,0,0,0,360,358,1,0,0,0,361,363,5,17,0,0,362,361,1,0,0,0, + 362,363,1,0,0,0,363,365,1,0,0,0,364,353,1,0,0,0,364,365,1,0,0,0,365,366, + 1,0,0,0,366,371,5,28,0,0,367,371,5,74,0,0,368,371,5,75,0,0,369,371,3,70, + 35,0,370,321,1,0,0,0,370,326,1,0,0,0,370,334,1,0,0,0,370,335,1,0,0,0,370, + 338,1,0,0,0,370,344,1,0,0,0,370,350,1,0,0,0,370,352,1,0,0,0,370,367,1,0, + 0,0,370,368,1,0,0,0,370,369,1,0,0,0,371,424,1,0,0,0,372,373,10,14,0,0,373, + 374,7,4,0,0,374,423,3,66,33,15,375,376,10,13,0,0,376,377,7,5,0,0,377,423, + 3,66,33,14,378,379,10,12,0,0,379,380,7,6,0,0,380,423,3,66,33,13,381,382, + 10,11,0,0,382,383,7,7,0,0,383,423,3,66,33,12,384,385,10,10,0,0,385,386, + 7,8,0,0,386,423,3,66,33,11,387,388,10,9,0,0,388,389,5,54,0,0,389,423,3, + 66,33,10,390,391,10,8,0,0,391,392,5,4,0,0,392,423,3,66,33,9,393,394,10, + 7,0,0,394,395,5,55,0,0,395,423,3,66,33,8,396,397,10,6,0,0,397,398,5,56, + 0,0,398,423,3,66,33,7,399,400,10,5,0,0,400,401,5,57,0,0,401,423,3,66,33, + 6,402,403,10,21,0,0,403,404,5,27,0,0,404,405,5,62,0,0,405,423,5,28,0,0, + 406,407,10,18,0,0,407,423,7,9,0,0,408,409,10,17,0,0,409,410,5,42,0,0,410, + 411,5,16,0,0,411,412,3,66,33,0,412,413,5,18,0,0,413,423,1,0,0,0,414,415, + 10,16,0,0,415,416,5,43,0,0,416,417,5,16,0,0,417,418,3,66,33,0,418,419,5, + 17,0,0,419,420,3,66,33,0,420,421,5,18,0,0,421,423,1,0,0,0,422,372,1,0,0, + 0,422,375,1,0,0,0,422,378,1,0,0,0,422,381,1,0,0,0,422,384,1,0,0,0,422,387, + 1,0,0,0,422,390,1,0,0,0,422,393,1,0,0,0,422,396,1,0,0,0,422,399,1,0,0,0, + 422,402,1,0,0,0,422,406,1,0,0,0,422,408,1,0,0,0,422,414,1,0,0,0,423,426, + 1,0,0,0,424,422,1,0,0,0,424,425,1,0,0,0,425,67,1,0,0,0,426,424,1,0,0,0, + 427,428,5,58,0,0,428,69,1,0,0,0,429,435,5,60,0,0,430,435,3,72,36,0,431, + 435,5,69,0,0,432,435,5,70,0,0,433,435,5,71,0,0,434,429,1,0,0,0,434,430, + 1,0,0,0,434,431,1,0,0,0,434,432,1,0,0,0,434,433,1,0,0,0,435,71,1,0,0,0, + 436,438,5,62,0,0,437,439,5,61,0,0,438,437,1,0,0,0,438,439,1,0,0,0,439,73, + 1,0,0,0,440,441,7,10,0,0,441,75,1,0,0,0,442,443,7,11,0,0,443,77,1,0,0,0, + 37,81,89,100,103,116,127,139,150,154,156,167,172,178,186,190,196,222,231, + 245,250,278,284,292,296,298,311,315,317,330,358,362,364,370,422,424,434, + 438]; private static __ATN: ATN; public static get _ATN(): ATN { @@ -2494,8 +2592,8 @@ export class SourceFileContext extends ParserRuleContext { super(parent, invokingState); this.parser = parser; } - public contractDefinition(): ContractDefinitionContext { - return this.getTypedRuleContext(ContractDefinitionContext, 0) as ContractDefinitionContext; + public topLevelDefinition(): TopLevelDefinitionContext { + return this.getTypedRuleContext(TopLevelDefinitionContext, 0) as TopLevelDefinitionContext; } public EOF(): TerminalNode { return this.getToken(CashScriptParser.EOF, 0); @@ -2520,6 +2618,31 @@ export class SourceFileContext extends ParserRuleContext { } +export class TopLevelDefinitionContext extends ParserRuleContext { + constructor(parser?: CashScriptParser, parent?: ParserRuleContext, invokingState?: number) { + super(parent, invokingState); + this.parser = parser; + } + public contractDefinition(): ContractDefinitionContext { + return this.getTypedRuleContext(ContractDefinitionContext, 0) as ContractDefinitionContext; + } + public libraryDefinition(): LibraryDefinitionContext { + return this.getTypedRuleContext(LibraryDefinitionContext, 0) as LibraryDefinitionContext; + } + public get ruleIndex(): number { + return CashScriptParser.RULE_topLevelDefinition; + } + // @Override + public accept(visitor: CashScriptVisitor): Result { + if (visitor.visitTopLevelDefinition) { + return visitor.visitTopLevelDefinition(this); + } else { + return visitor.visitChildren(this); + } + } +} + + export class PragmaDirectiveContext extends ParserRuleContext { constructor(parser?: CashScriptParser, parent?: ParserRuleContext, invokingState?: number) { super(parent, invokingState); @@ -2664,6 +2787,34 @@ export class ContractDefinitionContext extends ParserRuleContext { } +export class LibraryDefinitionContext extends ParserRuleContext { + constructor(parser?: CashScriptParser, parent?: ParserRuleContext, invokingState?: number) { + super(parent, invokingState); + this.parser = parser; + } + public Identifier(): TerminalNode { + return this.getToken(CashScriptParser.Identifier, 0); + } + public functionDefinition_list(): FunctionDefinitionContext[] { + return this.getTypedRuleContexts(FunctionDefinitionContext) as FunctionDefinitionContext[]; + } + public functionDefinition(i: number): FunctionDefinitionContext { + return this.getTypedRuleContext(FunctionDefinitionContext, i) as FunctionDefinitionContext; + } + public get ruleIndex(): number { + return CashScriptParser.RULE_libraryDefinition; + } + // @Override + public accept(visitor: CashScriptVisitor): Result { + if (visitor.visitLibraryDefinition) { + return visitor.visitLibraryDefinition(this); + } else { + return visitor.visitChildren(this); + } + } +} + + export class FunctionDefinitionContext extends ParserRuleContext { constructor(parser?: CashScriptParser, parent?: ParserRuleContext, invokingState?: number) { super(parent, invokingState); @@ -3319,7 +3470,7 @@ export class ExpressionContext extends ParserRuleContext { public get ruleIndex(): number { return CashScriptParser.RULE_expression; } - public copyFrom(ctx: ExpressionContext): void { + public override copyFrom(ctx: ExpressionContext): void { super.copyFrom(ctx); } } diff --git a/packages/cashc/src/grammar/CashScriptVisitor.ts b/packages/cashc/src/grammar/CashScriptVisitor.ts index c1d8ebfa..22e02762 100644 --- a/packages/cashc/src/grammar/CashScriptVisitor.ts +++ b/packages/cashc/src/grammar/CashScriptVisitor.ts @@ -1,15 +1,17 @@ -// Generated from src/grammar/CashScript.g4 by ANTLR 4.13.1 +// Generated from src/grammar/CashScript.g4 by ANTLR 4.13.2 import {ParseTreeVisitor} from 'antlr4'; import { SourceFileContext } from "./CashScriptParser.js"; +import { TopLevelDefinitionContext } from "./CashScriptParser.js"; import { PragmaDirectiveContext } from "./CashScriptParser.js"; import { PragmaNameContext } from "./CashScriptParser.js"; import { PragmaValueContext } from "./CashScriptParser.js"; import { VersionConstraintContext } from "./CashScriptParser.js"; import { VersionOperatorContext } from "./CashScriptParser.js"; import { ContractDefinitionContext } from "./CashScriptParser.js"; +import { LibraryDefinitionContext } from "./CashScriptParser.js"; import { FunctionDefinitionContext } from "./CashScriptParser.js"; import { ParameterListContext } from "./CashScriptParser.js"; import { ParameterContext } from "./CashScriptParser.js"; @@ -68,6 +70,12 @@ export default class CashScriptVisitor extends ParseTreeVisitor * @return the visitor result */ visitSourceFile?: (ctx: SourceFileContext) => Result; + /** + * Visit a parse tree produced by `CashScriptParser.topLevelDefinition`. + * @param ctx the parse tree + * @return the visitor result + */ + visitTopLevelDefinition?: (ctx: TopLevelDefinitionContext) => Result; /** * Visit a parse tree produced by `CashScriptParser.pragmaDirective`. * @param ctx the parse tree @@ -104,6 +112,12 @@ export default class CashScriptVisitor extends ParseTreeVisitor * @return the visitor result */ visitContractDefinition?: (ctx: ContractDefinitionContext) => Result; + /** + * Visit a parse tree produced by `CashScriptParser.libraryDefinition`. + * @param ctx the parse tree + * @return the visitor result + */ + visitLibraryDefinition?: (ctx: LibraryDefinitionContext) => Result; /** * Visit a parse tree produced by `CashScriptParser.functionDefinition`. * @param ctx the parse tree diff --git a/packages/cashc/src/imports.ts b/packages/cashc/src/imports.ts new file mode 100644 index 00000000..9a7150fe --- /dev/null +++ b/packages/cashc/src/imports.ts @@ -0,0 +1,871 @@ +import { CharStream, CommonTokenStream, Token } from 'antlr4'; +import fs from 'fs'; +import path from 'path'; +import semver from 'semver'; +import { fileURLToPath } from 'url'; +import { Class, FunctionVisibility, GLOBAL_SYMBOL_TABLE } from './ast/Globals.js'; +import { + ImportResolutionError, + InvalidImportDirectiveError, + InvalidLibraryImportError, + VersionError, +} from './Errors.js'; +import CashScriptLexer from './grammar/CashScriptLexer.js'; +import { version } from './index.js'; + +export interface ImportResolverResult { + source: string; + path?: string; +} + +export type ImportResolver = (specifier: string, from?: string) => ImportResolverResult | string; + +export interface ImportPreprocessOptions { + sourcePath?: string; + resolveImport?: ImportResolver; +} + +export interface ImportedFunctionProvenance { + mangledName: string; + generatedStartLine: number; + originalStartLine: number; + generatedStartColumn: number; + originalStartColumn: number; + source: string; + sourceFile?: string; +} + +export interface ImportPreprocessResult { + code: string; + sources: Array<{ source: string; sourceFile?: string }>; + functionProvenance: ImportedFunctionProvenance[]; +} + +interface ImportDirective { + specifier: string; + alias: string; + start: number; + stop: number; +} + +interface LibraryDefinition { + name: string; + body: string; + bodyStartLine: number; + source: string; + sourceFile?: string; +} + +interface ParsedLibraryFile { + imports: ImportDirective[]; + library: LibraryDefinition; + pragmaConstraints: string[]; +} + +interface ResolvedImport extends ImportResolverResult { + specifier: string; +} + +interface Replacement { + start: number; + stop: number; + text: string; +} + +interface CanonicalLibraryRecord { + sourceId: string; + mangledPrefix: string; + body: string; + functions: Set; + provenance: ImportedFunctionProvenance[]; + sources: Array<{ source: string; sourceFile?: string }>; +} + +interface ImportContext { + orderedLibraries: CanonicalLibraryRecord[]; + canonicalLibraries: Map; + usedMangledNames: Set; + nextLibraryId: number; +} + +const IDENTIFIER_PATTERN = /^[a-zA-Z][a-zA-Z0-9_]*$/; +const BUILT_IN_STATEMENTS = new Set(['require']); +const GLOBAL_FUNCTIONS = new Set( + Array.from(GLOBAL_SYMBOL_TABLE.symbols.values()) + .filter((symbol) => symbol.symbolType === 'function') + .map((symbol) => symbol.name), +); +const GLOBAL_CLASSES = new Set(Object.values(Class)); + +export function preprocessImports(code: string, options: ImportPreprocessOptions = {}): ImportPreprocessResult { + const importDirectives = parseTopLevelImports(code); + const rootSourceFile = options.sourcePath ? normaliseFilesystemPath(options.sourcePath) : undefined; + + if (importDirectives.length === 0) { + return { + code, + sources: [{ source: code, ...(rootSourceFile ? { sourceFile: rootSourceFile } : {}) }], + functionProvenance: [], + }; + } + + const mutableCode = code.split(''); + const rootFunctionNames = collectFunctionNames(code); + const usedAliases = new Set(); + const context: ImportContext = { + orderedLibraries: [], + canonicalLibraries: new Map(), + usedMangledNames: new Set(rootFunctionNames), + nextLibraryId: 0, + }; + + const transformedLibrariesByAlias = new Map(); + for (const directive of importDirectives) { + if (usedAliases.has(directive.alias)) { + throw new InvalidImportDirectiveError(`Duplicate import alias '${directive.alias}' is not allowed.`); + } + usedAliases.add(directive.alias); + + blankRangePreservingNewlines(mutableCode, directive.start, directive.stop); + + const transformedLibrary = preprocessImportedLibrary( + directive, + options, + context, + options.sourcePath, + [], + ); + transformedLibrariesByAlias.set(directive.alias, transformedLibrary); + } + + let flattenedCode = mutableCode.join(''); + transformedLibrariesByAlias.forEach((library, alias) => { + flattenedCode = rewriteNamespacedCalls(flattenedCode, alias, library.functions, library.mangledPrefix); + }); + + const contractCloseIndex = findRootCloseIndex(flattenedCode); + const injectedSections = context.orderedLibraries.map((library) => `\n${indentLibraryBody(library.body)}\n`); + const insertionBaseLine = countLines(flattenedCode.slice(0, contractCloseIndex)); + + let injectedLineOffset = 0; + const functionProvenance = context.orderedLibraries.flatMap((library, index) => { + const section = injectedSections[index]!; + const resolved = library.provenance.map((entry) => ({ + ...entry, + generatedStartLine: insertionBaseLine + injectedLineOffset + getFunctionGeneratedLine(entry.mangledName, section), + })); + injectedLineOffset += countLineBreaks(section) + 1; + return resolved; + }); + + const sources = dedupeSources([ + { source: code, ...(rootSourceFile ? { sourceFile: rootSourceFile } : {}) }, + ...context.orderedLibraries.flatMap((library) => library.sources), + ]); + + return { + code: `${flattenedCode.slice(0, contractCloseIndex)}${injectedSections.join('\n')}${flattenedCode.slice(contractCloseIndex)}`, + sources, + functionProvenance, + }; +} + +function preprocessImportedLibrary( + directive: ImportDirective, + options: ImportPreprocessOptions, + context: ImportContext, + fromPath?: string, + ancestry: string[] = [], +): CanonicalLibraryRecord { + const resolvedImport = resolveImportSource(directive.specifier, options, fromPath); + const sourceId = getImportIdentity(resolvedImport, directive.specifier, fromPath); + if (ancestry.includes(sourceId)) { + throw new InvalidLibraryImportError( + `Circular library import detected: ${[...ancestry, sourceId].join(' -> ')}`, + ); + } + + const cached = context.canonicalLibraries.get(sourceId); + if (cached) { + return cached; + } + + const parsedLibrary = parseLibraryFileWithPragmas(resolvedImport.source, resolvedImport.path ?? directive.specifier); + validateLibraryPragmas(parsedLibrary.pragmaConstraints); + + const nestedLibrariesByAlias = new Map(); + const usedNestedAliases = new Set(); + for (const nestedImport of parsedLibrary.imports) { + if (usedNestedAliases.has(nestedImport.alias)) { + throw new InvalidImportDirectiveError(`Duplicate import alias '${nestedImport.alias}' is not allowed.`); + } + usedNestedAliases.add(nestedImport.alias); + nestedLibrariesByAlias.set( + nestedImport.alias, + preprocessImportedLibrary( + nestedImport, + options, + context, + resolvedImport.path ?? fromPath, + [...ancestry, sourceId], + ), + ); + } + + const mangledPrefix = `lib${context.nextLibraryId}`; + context.nextLibraryId += 1; + const transformed = transformLibrary( + parsedLibrary.library, + mangledPrefix, + context.usedMangledNames, + nestedLibrariesByAlias, + ); + + const record: CanonicalLibraryRecord = { + sourceId, + mangledPrefix, + body: transformed.body, + functions: transformed.functions, + provenance: transformed.provenance, + sources: transformed.sources, + }; + + context.canonicalLibraries.set(sourceId, record); + context.orderedLibraries.push(record); + return record; +} + +function resolveImportSource(specifier: string, options: ImportPreprocessOptions, fromPath?: string): ResolvedImport { + if (options.resolveImport) { + const result = options.resolveImport(specifier, fromPath ?? options.sourcePath); + if (typeof result === 'string') { + return { source: result, specifier }; + } + + return { ...result, specifier }; + } + + const resolutionBasePath = fromPath ?? options.sourcePath; + if (!resolutionBasePath) { + throw new ImportResolutionError( + `Cannot resolve import '${specifier}' without a sourcePath or resolveImport callback.`, + ); + } + + if (!specifier.startsWith('./') && !specifier.startsWith('../')) { + throw new InvalidImportDirectiveError( + `Only relative import specifiers are supported. Found '${specifier}'.`, + ); + } + + const resolvedPath = path.resolve(path.dirname(normaliseFilesystemPath(resolutionBasePath)), specifier); + if (!fs.existsSync(resolvedPath)) { + throw new ImportResolutionError(`Unable to resolve import '${specifier}' from '${resolutionBasePath}'.`); + } + + return { + specifier, + source: fs.readFileSync(resolvedPath, { encoding: 'utf-8' }), + path: resolvedPath, + }; +} + +function parseTopLevelImports(code: string): ImportDirective[] { + const tokens = getVisibleTokens(code); + const imports: ImportDirective[] = []; + let cursor = 0; + + while (cursor < tokens.length) { + const token = tokens[cursor]; + + if (token.text === 'pragma') { + cursor = advanceToSemicolon(tokens, cursor + 1); + continue; + } + + if (token.text === 'import') { + const specifierToken = tokens[cursor + 1]; + const asToken = tokens[cursor + 2]; + const aliasToken = tokens[cursor + 3]; + const semicolonToken = tokens[cursor + 4]; + + if ( + !specifierToken?.text + || !isStringLiteral(specifierToken.text) + || asToken?.text !== 'as' + || !aliasToken?.text?.match(IDENTIFIER_PATTERN) + || semicolonToken?.text !== ';' + ) { + throw new InvalidImportDirectiveError( + 'Import directives must use the form import "./helpers.cash" as Helpers;', + ); + } + + imports.push({ + specifier: parseStringLiteral(specifierToken.text), + alias: aliasToken.text, + start: token.start, + stop: semicolonToken.stop, + }); + + cursor += 5; + continue; + } + + if (token.text === 'contract' || token.text === 'library') { + break; + } + + break; + } + + const misplacedImport = tokens.slice(cursor).find((token) => token.text === 'import'); + if (misplacedImport) { + throw new InvalidImportDirectiveError('Import directives must appear before the root contract or library definition.'); + } + + return imports; +} + +function parseLibraryFileWithPragmas(code: string, sourceLabel: string): ParsedLibraryFile { + const tokens = getVisibleTokens(code); + + let cursor = 0; + const pragmaConstraints: string[] = []; + while (cursor < tokens.length && tokens[cursor].text === 'pragma') { + pragmaConstraints.push(...readPragmaConstraints(tokens, cursor, sourceLabel)); + cursor = advanceToSemicolon(tokens, cursor + 1); + } + + const nestedImports = parseTopLevelImports(code); + while (cursor < tokens.length && tokens[cursor].text === 'import') { + cursor = advanceToSemicolon(tokens, cursor + 1); + } + + const libraryToken = tokens[cursor]; + const nameToken = tokens[cursor + 1]; + const openBraceToken = tokens[cursor + 2]; + + if (libraryToken?.text !== 'library' || !nameToken?.text?.match(IDENTIFIER_PATTERN) || openBraceToken?.text !== '{') { + throw new InvalidLibraryImportError( + `Imported file '${sourceLabel}' must contain exactly one top-level library.`, + ); + } + + const closeBraceIndex = findMatchingBrace(tokens, cursor + 2); + const closeBraceToken = tokens[closeBraceIndex]; + const trailingTokens = tokens.slice(closeBraceIndex + 1); + if (trailingTokens.length !== 0) { + throw new InvalidLibraryImportError( + `Imported file '${sourceLabel}' may only contain a single library definition.`, + ); + } + + return { + imports: nestedImports, + library: { + name: nameToken.text, + body: code.slice(openBraceToken.stop + 1, closeBraceToken.start), + bodyStartLine: openBraceToken.line, + source: code, + ...(sourceLabel ? { sourceFile: sourceLabel } : {}), + }, + pragmaConstraints, + }; +} + +function transformLibrary( + library: LibraryDefinition, + mangledPrefix: string, + usedMangledNames: Set, + nestedLibrariesByAlias: Map, +): { + body: string; + functions: Set; + provenance: ImportedFunctionProvenance[]; + sources: Array<{ source: string; sourceFile?: string }>; + } { + let rewrittenBody = library.body; + const nestedAccessibleFunctions = new Set(); + + nestedLibrariesByAlias.forEach((nestedLibrary, nestedAlias) => { + rewrittenBody = rewriteNamespacedCalls( + rewrittenBody, + nestedAlias, + nestedLibrary.functions, + nestedLibrary.mangledPrefix, + ); + + nestedLibrary.functions.forEach((functionName) => { + nestedAccessibleFunctions.add(`${nestedLibrary.mangledPrefix}_${functionName}`); + }); + }); + + const tokens = getVisibleTokens(rewrittenBody); + const functionDefinitions = collectFunctionDefinitions(tokens); + + if (functionDefinitions.length === 0) { + throw new InvalidLibraryImportError(`Library '${library.name}' does not define any functions.`); + } + + const localFunctions = new Set(functionDefinitions.map((definition) => definition.name)); + const mangledNames = new Map(); + const replacements: Replacement[] = []; + + functionDefinitions.forEach(({ name, nameToken }) => { + const mangledName = `${mangledPrefix}_${name}`; + if (usedMangledNames.has(mangledName)) { + throw new InvalidLibraryImportError( + `Imported function '${library.name}.${name}' conflicts with an existing function named '${mangledName}'.`, + ); + } + + usedMangledNames.add(mangledName); + mangledNames.set(name, mangledName); + replacements.push({ start: nameToken.start, stop: nameToken.stop, text: mangledName }); + }); + + validateLibraryCalls(tokens, new Set([...localFunctions, ...nestedAccessibleFunctions]), library.name); + replacements.push(...collectLocalFunctionCallReplacements(tokens, mangledNames)); + + const body = applyReplacements(rewrittenBody, replacements).trim(); + return { + body, + functions: new Set(functionDefinitions.map((definition) => definition.name)), + provenance: functionDefinitions.map((definition) => ({ + mangledName: mangledNames.get(definition.name)!, + generatedStartLine: 0, + originalStartLine: library.bodyStartLine + definition.line - 1, + generatedStartColumn: definition.functionToken.column + 2, + originalStartColumn: definition.functionToken.column, + source: library.source, + ...(library.sourceFile ? { sourceFile: library.sourceFile } : {}), + })), + sources: [{ source: library.source, ...(library.sourceFile ? { sourceFile: library.sourceFile } : {}) }], + }; +} + +function collectFunctionDefinitions(tokens: Token[]): Array<{ + functionToken: Token; + name: string; + nameToken: Token; + visibility: FunctionVisibility.INTERNAL; + line: number; +}> { + const definitions: Array<{ + functionToken: Token; + name: string; + nameToken: Token; + visibility: FunctionVisibility.INTERNAL; + line: number; + }> = []; + + for (let index = 0; index < tokens.length; index += 1) { + if (tokens[index].text !== 'function') continue; + + const nameToken = tokens[index + 1]; + const openParenToken = tokens[index + 2]; + if (!nameToken?.text?.match(IDENTIFIER_PATTERN) || openParenToken?.text !== '(') { + throw new InvalidLibraryImportError('Invalid function definition in imported library.'); + } + + let cursor = index + 3; + let depth = 1; + while (cursor < tokens.length && depth > 0) { + if (tokens[cursor].text === '(') depth += 1; + if (tokens[cursor].text === ')') depth -= 1; + cursor += 1; + } + + const visibilityToken = tokens[cursor]; + if (visibilityToken?.text !== FunctionVisibility.INTERNAL) { + throw new InvalidLibraryImportError( + `Imported library functions must declare internal visibility. Offending function: '${nameToken.text}'.`, + ); + } + + const openBraceToken = tokens[cursor + 1]; + if (openBraceToken?.text !== '{') { + throw new InvalidLibraryImportError('Invalid function definition in imported library.'); + } + + definitions.push({ + functionToken: tokens[index], + name: nameToken.text, + nameToken, + visibility: FunctionVisibility.INTERNAL, + line: nameToken.line, + }); + } + + return definitions; +} + +function validateLibraryCalls(tokens: Token[], accessibleFunctions: Set, libraryName: string): void { + for (let index = 0; index < tokens.length - 1; index += 1) { + const token = tokens[index]; + const nextToken = tokens[index + 1]; + const previousToken = tokens[index - 1]; + + if ( + token.text?.match(IDENTIFIER_PATTERN) + && nextToken?.text === '.' + && tokens[index + 2]?.text?.match(IDENTIFIER_PATTERN) + && tokens[index + 3]?.text === '(' + && previousToken?.text !== '.' + ) { + throw new InvalidLibraryImportError( + `Library '${libraryName}' references external helper '${token.text}.${tokens[index + 2]!.text}'. Imported libraries may only call imported or local helper functions.`, + ); + } + + if (!token.text?.match(IDENTIFIER_PATTERN) || nextToken?.text !== '(') continue; + if (previousToken?.text === 'function' || previousToken?.text === 'new' || previousToken?.text === '.') continue; + + if ( + accessibleFunctions.has(token.text) + || GLOBAL_FUNCTIONS.has(token.text) + || GLOBAL_CLASSES.has(token.text as Class) + || BUILT_IN_STATEMENTS.has(token.text) + ) { + continue; + } + + throw new InvalidLibraryImportError( + `Library '${libraryName}' references non-library function '${token.text}'. Imported libraries may only call imported or local helper functions.`, + ); + } +} + +function collectLocalFunctionCallReplacements(tokens: Token[], mangledNames: Map): Replacement[] { + const replacements: Replacement[] = []; + + for (let index = 0; index < tokens.length - 1; index += 1) { + const token = tokens[index]; + const nextToken = tokens[index + 1]; + const previousToken = tokens[index - 1]; + + if (!token.text?.match(IDENTIFIER_PATTERN) || nextToken?.text !== '(') continue; + if (previousToken?.text === 'function' || previousToken?.text === 'new' || previousToken?.text === '.') continue; + + const mangledName = mangledNames.get(token.text); + if (!mangledName) continue; + + replacements.push({ start: token.start, stop: token.stop, text: mangledName }); + } + + return replacements; +} + +function rewriteNamespacedCalls( + code: string, + alias: string, + functions: Set, + replacementPrefix: string = alias, +): string { + let output = ''; + + for (let index = 0; index < code.length;) { + if (startsWithLineComment(code, index)) { + const commentEnd = findLineCommentEnd(code, index); + output += code.slice(index, commentEnd); + index = commentEnd; + continue; + } + + if (startsWithBlockComment(code, index)) { + const commentEnd = findBlockCommentEnd(code, index); + output += code.slice(index, commentEnd); + index = commentEnd; + continue; + } + + if (code[index] === '"' || code[index] === '\'') { + const stringEnd = findStringEnd(code, index); + output += code.slice(index, stringEnd); + index = stringEnd; + continue; + } + + const rewrite = tryRewriteNamespacedCall(code, index, alias, functions, replacementPrefix); + if (rewrite) { + output += rewrite.replacement; + index = rewrite.nextIndex; + continue; + } + + output += code[index]; + index += 1; + } + + return output; +} + +function collectFunctionNames(code: string): Set { + const names = new Set(); + const tokens = getVisibleTokens(code); + + for (let index = 0; index < tokens.length - 2; index += 1) { + if (tokens[index].text !== 'function') continue; + + const nameToken = tokens[index + 1]; + const openParenToken = tokens[index + 2]; + if (nameToken?.text?.match(IDENTIFIER_PATTERN) && openParenToken?.text === '(') { + names.add(nameToken.text); + } + } + + return names; +} + +function findRootCloseIndex(code: string): number { + const tokens = getVisibleTokens(code); + const rootIndex = tokens.findIndex((token) => token.text === 'contract' || token.text === 'library'); + if (rootIndex === -1) { + throw new InvalidImportDirectiveError('Imports require a root contract or library definition.'); + } + + const openBraceIndex = tokens.findIndex((token, index) => index > rootIndex && token.text === '{'); + if (openBraceIndex === -1) { + throw new InvalidImportDirectiveError('Unable to locate the root body.'); + } + + return tokens[findMatchingBrace(tokens, openBraceIndex)].start; +} + +function indentLibraryBody(body: string): string { + return body + .split('\n') + .map((line) => (line.length === 0 ? line : ` ${line}`)) + .join('\n'); +} + +function countLineBreaks(text: string): number { + return (text.match(/\n/g) ?? []).length; +} + +function countLines(text: string): number { + return countLineBreaks(text) + 1; +} + +function getFunctionGeneratedLine(mangledName: string, section: string): number { + const index = section.indexOf(mangledName); + if (index === -1) { + throw new InvalidLibraryImportError(`Could not map imported helper '${mangledName}' back to generated source.`); + } + + return countLineBreaks(section.slice(0, index)) + 1; +} + +function dedupeSources( + sources: Array<{ source: string; sourceFile?: string }>, +): Array<{ source: string; sourceFile?: string }> { + const seen = new Set(); + return sources.filter((entry) => { + const key = `${entry.sourceFile ?? ''}\u0000${entry.source}`; + if (seen.has(key)) return false; + seen.add(key); + return true; + }); +} + +function getImportIdentity(resolvedImport: ResolvedImport, specifier: string, fromPath?: string): string { + if (resolvedImport.path) return normaliseFilesystemPath(resolvedImport.path); + return `${fromPath ?? ''}::${specifier}`; +} + +function findMatchingBrace(tokens: Token[], openBraceIndex: number): number { + let depth = 0; + for (let index = openBraceIndex; index < tokens.length; index += 1) { + if (tokens[index].text === '{') depth += 1; + if (tokens[index].text === '}') { + depth -= 1; + if (depth === 0) return index; + } + } + + throw new InvalidImportDirectiveError('Could not match braces while preprocessing imports.'); +} + +function advanceToSemicolon(tokens: Token[], cursor: number): number { + while (cursor < tokens.length && tokens[cursor].text !== ';') { + cursor += 1; + } + + if (tokens[cursor]?.text !== ';') { + throw new InvalidImportDirectiveError('Expected semicolon while preprocessing imports.'); + } + + return cursor + 1; +} + +function blankRangePreservingNewlines(chars: string[], start: number, stop: number): void { + for (let index = start; index <= stop; index += 1) { + if (chars[index] !== '\n' && chars[index] !== '\r') { + chars[index] = ' '; + } + } +} + +function applyReplacements(code: string, replacements: Replacement[]): string { + const orderedReplacements = [...replacements].sort((left, right) => right.start - left.start); + let output = code; + + orderedReplacements.forEach(({ start, stop, text }) => { + output = `${output.slice(0, start)}${text}${output.slice(stop + 1)}`; + }); + + return output; +} + +function getVisibleTokens(code: string): Token[] { + const inputStream = new CharStream(code); + const lexer = new CashScriptLexer(inputStream); + lexer.removeErrorListeners(); + const tokenStream = new CommonTokenStream(lexer); + tokenStream.fill(); + + return tokenStream.tokens.filter((token) => token.channel === 0 && token.type !== -1); +} + +function tryRewriteNamespacedCall( + code: string, + index: number, + alias: string, + functions: Set, + replacementPrefix: string, +): { replacement: string; nextIndex: number } | undefined { + if (!code.startsWith(alias, index)) return undefined; + if (!isIdentifierBoundary(code[index - 1])) return undefined; + if (!isIdentifierBoundary(code[index + alias.length])) return undefined; + + let cursor = skipWhitespace(code, index + alias.length); + if (code[cursor] !== '.') return undefined; + + cursor = skipWhitespace(code, cursor + 1); + const functionNameMatch = /^[a-zA-Z][a-zA-Z0-9_]*/.exec(code.slice(cursor)); + if (!functionNameMatch) { + throw new InvalidImportDirectiveError(`Invalid imported function call '${alias}.' in contract source.`); + } + + const functionName = functionNameMatch[0]; + if (!functions.has(functionName)) { + throw new InvalidImportDirectiveError(`Imported library '${alias}' has no function named '${functionName}'.`); + } + + cursor = skipWhitespace(code, cursor + functionName.length); + if (code[cursor] !== '(') { + throw new InvalidImportDirectiveError( + `Imported library function calls must use the form '${alias}.${functionName}(...)'.`, + ); + } + + return { + replacement: `${replacementPrefix}_${functionName}`, + nextIndex: cursor, + }; +} + +function skipWhitespace(code: string, index: number): number { + let cursor = index; + while (cursor < code.length && /\s/.test(code[cursor])) { + cursor += 1; + } + return cursor; +} + +function startsWithLineComment(code: string, index: number): boolean { + return code[index] === '/' && code[index + 1] === '/'; +} + +function startsWithBlockComment(code: string, index: number): boolean { + return code[index] === '/' && code[index + 1] === '*'; +} + +function findLineCommentEnd(code: string, index: number): number { + let cursor = index; + while (cursor < code.length && code[cursor] !== '\n') { + cursor += 1; + } + return cursor; +} + +function findBlockCommentEnd(code: string, index: number): number { + let cursor = index + 2; + while (cursor < code.length && !(code[cursor] === '*' && code[cursor + 1] === '/')) { + cursor += 1; + } + return cursor < code.length ? cursor + 2 : code.length; +} + +function findStringEnd(code: string, index: number): number { + const quote = code[index]; + let cursor = index + 1; + while (cursor < code.length) { + if (code[cursor] === '\\') { + cursor += 2; + continue; + } + if (code[cursor] === quote) { + return cursor + 1; + } + cursor += 1; + } + return code.length; +} + +function isIdentifierBoundary(char: string | undefined): boolean { + return char === undefined || !/[a-zA-Z0-9_]/.test(char); +} + +function isStringLiteral(value: string): boolean { + return (value.startsWith('"') && value.endsWith('"')) || (value.startsWith('\'') && value.endsWith('\'')); +} + +function parseStringLiteral(value: string): string { + return JSON.parse(value.replace(/^'/, '"').replace(/'$/, '"')); +} + +function readPragmaConstraints(tokens: Token[], pragmaIndex: number, sourceLabel: string): string[] { + const nameToken = tokens[pragmaIndex + 1]; + if (nameToken?.text !== 'cashscript') { + throw new VersionError(sourceLabel, 'pragma cashscript ...'); + } + + const constraints: string[] = []; + let cursor = pragmaIndex + 2; + while (tokens[cursor]?.text !== ';') { + const operator = tokens[cursor]?.text?.match(/^[\^~><=]+$/) ? tokens[cursor]!.text : ''; + if (operator) cursor += 1; + + const versionToken = tokens[cursor]; + if (!versionToken?.text) { + throw new VersionError(sourceLabel, 'valid pragma cashscript version constraint'); + } + + constraints.push(`${operator}${versionToken.text}`); + cursor += 1; + } + + return constraints; +} + +function validateLibraryPragmas(constraints: string[]): void { + constraints.forEach((constraint) => { + if (!semver.satisfies(version, constraint, { includePrerelease: true })) { + throw new VersionError(version, constraint); + } + }); +} + +function normaliseFilesystemPath(codeFile: string | URL): string { + if (codeFile instanceof URL) { + return fileURLToPath(codeFile); + } + + if (codeFile.startsWith('file://')) { + return fileURLToPath(codeFile); + } + + return codeFile; +} diff --git a/packages/cashc/src/index.ts b/packages/cashc/src/index.ts index 6c07b9eb..55265484 100644 --- a/packages/cashc/src/index.ts +++ b/packages/cashc/src/index.ts @@ -1,5 +1,7 @@ export * from './Errors.js'; export * as utils from '@cashscript/utils'; -export { compileFile, compileString } from './compiler.js'; +export type { CompileOptions } from './compiler.js'; +export type { ImportResolver, ImportResolverResult } from './imports.js'; +export { compileFile, compileString, parseCode } from './compiler.js'; export const version = '0.13.0-next.6'; diff --git a/packages/cashc/src/print/OutputSourceCodeTraversal.ts b/packages/cashc/src/print/OutputSourceCodeTraversal.ts index 70f62db2..56a6fca2 100644 --- a/packages/cashc/src/print/OutputSourceCodeTraversal.ts +++ b/packages/cashc/src/print/OutputSourceCodeTraversal.ts @@ -62,9 +62,18 @@ export default class OutputSourceCodeTraversal extends AstTraversal { } visitContract(node: ContractNode): Node { - this.addOutput(`contract ${node.name}(`, true); - node.parameters = this.visitCommaList(node.parameters) as ParameterNode[]; - this.addOutput(') {'); + this.addOutput(`${node.kind} ${node.name}`, true); + + if (node.kind === 'contract') { + this.addOutput('('); + node.parameters = this.visitCommaList(node.parameters) as ParameterNode[]; + this.addOutput(') '); + } else { + node.parameters = this.visitCommaList(node.parameters) as ParameterNode[]; + this.addOutput(' '); + } + + this.addOutput('{'); this.outputSymbolTable(node.symbolTable); this.addOutput('\n'); @@ -79,7 +88,7 @@ export default class OutputSourceCodeTraversal extends AstTraversal { visitFunctionDefinition(node: FunctionDefinitionNode): Node { this.addOutput(`function ${node.name}(`, true); node.parameters = this.visitCommaList(node.parameters) as ParameterNode[]; - this.addOutput(')'); + this.addOutput(`) ${node.visibility}`); this.outputSymbolTable(node.symbolTable); this.addOutput(' '); diff --git a/packages/cashc/src/semantic/EnsureContainerSemanticsTraversal.ts b/packages/cashc/src/semantic/EnsureContainerSemanticsTraversal.ts new file mode 100644 index 00000000..9661e4a7 --- /dev/null +++ b/packages/cashc/src/semantic/EnsureContainerSemanticsTraversal.ts @@ -0,0 +1,34 @@ +import { + ContractNode, + FunctionDefinitionNode, + ParameterNode, + Node, +} from '../ast/AST.js'; +import AstTraversal from '../ast/AstTraversal.js'; +import { FunctionVisibility } from '../ast/Globals.js'; +import { + LibraryParameterError, + LibraryPublicFunctionError, +} from '../Errors.js'; + +export default class EnsureContainerSemanticsTraversal extends AstTraversal { + visitContract(node: ContractNode): Node { + node.parameters = this.visitList(node.parameters) as ParameterNode[]; + node.functions = this.visitList(node.functions) as FunctionDefinitionNode[]; + + if (node.kind !== 'library') { + return node; + } + + if (node.parameters.length > 0) { + throw new LibraryParameterError(node); + } + + const publicFunction = node.functions.find((func) => func.visibility === FunctionVisibility.PUBLIC); + if (publicFunction) { + throw new LibraryPublicFunctionError(publicFunction); + } + + return node; + } +} diff --git a/packages/cashc/src/semantic/EnsureFinalRequireTraversal.ts b/packages/cashc/src/semantic/EnsureFinalRequireTraversal.ts index 5bb1d4bf..9114e053 100644 --- a/packages/cashc/src/semantic/EnsureFinalRequireTraversal.ts +++ b/packages/cashc/src/semantic/EnsureFinalRequireTraversal.ts @@ -13,13 +13,14 @@ import { } from '../ast/AST.js'; import AstTraversal from '../ast/AstTraversal.js'; import { EmptyContractError, EmptyFunctionError, FinalRequireStatementError } from '../Errors.js'; +import { getPublicFunctions } from '../utils.js'; export default class EnsureFinalRequireTraversal extends AstTraversal { visitContract(node: ContractNode): ContractNode { node.parameters = this.visitList(node.parameters) as ParameterNode[]; node.functions = this.visitList(node.functions) as FunctionDefinitionNode[]; - if (node.functions.length === 0) { + if (node.kind !== 'library' && getPublicFunctions(node.functions).length === 0) { throw new EmptyContractError(node); } diff --git a/packages/cashc/src/semantic/EnsureInvokedFunctionsSafeTraversal.ts b/packages/cashc/src/semantic/EnsureInvokedFunctionsSafeTraversal.ts new file mode 100644 index 00000000..87b00597 --- /dev/null +++ b/packages/cashc/src/semantic/EnsureInvokedFunctionsSafeTraversal.ts @@ -0,0 +1,90 @@ +import { + ContractNode, + FunctionCallNode, + FunctionDefinitionNode, + IdentifierNode, + Node, + ParameterNode, +} from '../ast/AST.js'; +import AstTraversal from '../ast/AstTraversal.js'; +import { GlobalFunction } from '../ast/Globals.js'; +import { + CircularFunctionDependencyError, + InvokedFunctionContractParameterReferenceError, + InvokedFunctionSignatureCheckError, +} from '../Errors.js'; +import { getInvokedFunctionClosure } from '../utils.js'; + +const SIGNATURE_FUNCTIONS = new Set([ + GlobalFunction.CHECKSIG, + GlobalFunction.CHECKMULTISIG, + GlobalFunction.CHECKDATASIG, +]); + +export default class EnsureInvokedFunctionsSafeTraversal extends AstTraversal { + private invokedFunctions = new Set(); + private contractParameterNames = new Set(); + private currentFunction?: FunctionDefinitionNode; + + visitContract(node: ContractNode): Node { + this.contractParameterNames = new Set(node.parameters.map((parameter) => parameter.name)); + this.invokedFunctions = getInvokedFunctionClosure(node.functions); + this.ensureAcyclicFunctionCalls(node.functions); + + return super.visitContract(node); + } + + visitFunctionDefinition(node: FunctionDefinitionNode): Node { + const previousFunction = this.currentFunction; + this.currentFunction = node; + + const result = this.invokedFunctions.has(node.name) ? super.visitFunctionDefinition(node) : node; + + this.currentFunction = previousFunction; + return result; + } + + visitFunctionCall(node: FunctionCallNode): Node { + if (SIGNATURE_FUNCTIONS.has(node.identifier.name as GlobalFunction)) { + throw new InvokedFunctionSignatureCheckError(node); + } + + return super.visitFunctionCall(node); + } + + visitIdentifier(node: IdentifierNode): Node { + if ( + this.currentFunction + && this.invokedFunctions.has(this.currentFunction.name) + && this.contractParameterNames.has(node.name) + && node.definition?.definition instanceof ParameterNode + ) { + throw new InvokedFunctionContractParameterReferenceError(node); + } + + return super.visitIdentifier(node); + } + + private ensureAcyclicFunctionCalls(functions: FunctionDefinitionNode[]): void { + const functionsByName = new Map(functions.map((func) => [func.name, func])); + const visiting = new Set(); + const visited = new Set(); + + const visit = (func: FunctionDefinitionNode): void => { + if (visited.has(func.name)) return; + if (visiting.has(func.name)) { + throw new CircularFunctionDependencyError(func); + } + + visiting.add(func.name); + func.calledFunctions.forEach((calledFunctionName) => { + const calledFunction = functionsByName.get(calledFunctionName); + if (calledFunction) visit(calledFunction); + }); + visiting.delete(func.name); + visited.add(func.name); + }; + + functions.forEach(visit); + } +} diff --git a/packages/cashc/src/semantic/SymbolTableTraversal.ts b/packages/cashc/src/semantic/SymbolTableTraversal.ts index badf28f6..658142a0 100644 --- a/packages/cashc/src/semantic/SymbolTableTraversal.ts +++ b/packages/cashc/src/semantic/SymbolTableTraversal.ts @@ -1,4 +1,5 @@ import { GLOBAL_SYMBOL_TABLE, Modifier } from '../ast/Globals.js'; +import { PrimitiveType } from '@cashscript/utils'; import { ContractNode, ParameterNode, @@ -29,7 +30,7 @@ import { export default class SymbolTableTraversal extends AstTraversal { private symbolTables: SymbolTable[] = [GLOBAL_SYMBOL_TABLE]; - private functionNames: Map = new Map(); + private functionDefinitions: Map = new Map(); private currentFunction: FunctionDefinitionNode; private expectedSymbolType: SymbolType = SymbolType.VARIABLE; private insideConsoleStatement: boolean = false; @@ -39,6 +40,20 @@ export default class SymbolTableTraversal extends AstTraversal { this.symbolTables.unshift(node.symbolTable); node.parameters = this.visitList(node.parameters) as ParameterNode[]; + + node.functions.forEach((func) => { + if (this.functionDefinitions.get(func.name) || GLOBAL_SYMBOL_TABLE.get(func.name)) { + throw new FunctionRedefinitionError(func); + } + + this.functionDefinitions.set(func.name, Symbol.definedFunction( + func.name, + PrimitiveType.BOOL, + func, + func.parameters.map((parameter) => parameter.type), + )); + }); + node.functions = this.visitList(node.functions) as FunctionDefinitionNode[]; const unusedSymbols = node.symbolTable.unusedSymbols(); @@ -47,6 +62,7 @@ export default class SymbolTableTraversal extends AstTraversal { } this.symbolTables.shift(); + this.functionDefinitions.clear(); return node; } @@ -62,13 +78,6 @@ export default class SymbolTableTraversal extends AstTraversal { visitFunctionDefinition(node: FunctionDefinitionNode): Node { this.currentFunction = node; - // Checked for function redefinition, but they are not included in the - // symbol table, as internal function calls are not supported. - if (this.functionNames.get(node.name)) { - throw new FunctionRedefinitionError(node); - } - this.functionNames.set(node.name, true); - node.symbolTable = new SymbolTable(this.symbolTables[0]); this.symbolTables.unshift(node.symbolTable); @@ -157,10 +166,24 @@ export default class SymbolTableTraversal extends AstTraversal { } visitFunctionCall(node: FunctionCallNode): Node { - this.expectedSymbolType = SymbolType.FUNCTION; - node.identifier = this.visit(node.identifier) as IdentifierNode; - this.expectedSymbolType = SymbolType.VARIABLE; + const symbol = this.symbolTables[0].get(node.identifier.name); + if (symbol?.symbolType === SymbolType.FUNCTION) { + node.identifier.definition = symbol; + node.identifier.definition.references.push(node.identifier); + } else if (this.functionDefinitions.has(node.identifier.name)) { + node.identifier.definition = this.functionDefinitions.get(node.identifier.name)!; + } else if (symbol) { + throw new InvalidSymbolTypeError(node.identifier, SymbolType.FUNCTION); + } else { + throw new UndefinedReferenceError(node.identifier); + } + node.parameters = this.visitList(node.parameters); + + if (node.identifier.definition?.definition instanceof FunctionDefinitionNode) { + this.currentFunction.calledFunctions.add(node.identifier.name); + } + return node; } diff --git a/packages/cashc/src/utils.ts b/packages/cashc/src/utils.ts index c24a140d..280417d4 100644 --- a/packages/cashc/src/utils.ts +++ b/packages/cashc/src/utils.ts @@ -1,5 +1,7 @@ import { BytesType, implicitlyCastable, PrimitiveType, Type } from '@cashscript/utils'; import { BinaryOperator } from './ast/Operator.js'; +import { FunctionDefinitionNode } from './ast/AST.js'; +import { FunctionVisibility } from './ast/Globals.js'; export function resultingTypeForBinaryOp( operator: BinaryOperator, @@ -20,3 +22,31 @@ export function resultingTypeForBinaryOp( export function isNumericType(type?: Type): boolean { return type === PrimitiveType.INT || type === PrimitiveType.BOOL; } + +export function getPublicFunctions( + functions: FunctionDefinitionNode[], +): FunctionDefinitionNode[] { + return functions.filter((func) => func.visibility === FunctionVisibility.PUBLIC); +} + +export function getInvokedFunctionClosure( + functions: FunctionDefinitionNode[], +): Set { + const functionsByName = new Map(functions.map((func) => [func.name, func])); + const reachableFunctions = new Set(); + const pending = getPublicFunctions(functions) + .flatMap((func) => Array.from(func.calledFunctions)); + + while (pending.length > 0) { + const functionName = pending.pop()!; + if (reachableFunctions.has(functionName)) continue; + + reachableFunctions.add(functionName); + const definition = functionsByName.get(functionName); + if (!definition) continue; + + pending.push(...definition.calledFunctions); + } + + return reachableFunctions; +} diff --git a/packages/cashc/test/ast/AST.test.ts b/packages/cashc/test/ast/AST.test.ts index 7a7c0076..18a3bcdd 100644 --- a/packages/cashc/test/ast/AST.test.ts +++ b/packages/cashc/test/ast/AST.test.ts @@ -52,5 +52,46 @@ describe('AST Builder', () => { expect(rerunOutput).toEqual(initialOutput); }); }); + + it('should preserve explicit function visibility in AST source output', () => { + const input = ` +contract Test() { + function spend(int value) public { + require(validate(value)); + } + + function validate(int value) internal { + require(value == 1); + } +}`; + + const { sourceOutput: initialOutput } = setup(input); + const { sourceOutput: rerunOutput } = setup(initialOutput); + + expect(initialOutput).toContain('function spend(int value) public'); + expect(initialOutput).toContain('function validate(int value) internal'); + expect(rerunOutput).toEqual(initialOutput); + }); + + it('should preserve library containers in AST source output', () => { + const input = ` +library MathHelpers { + function isEven(int value) internal { + require(value % 2 == 0); + } + + function checkParity(int value) internal { + require(isEven(value)); + } +}`; + + const { sourceOutput: initialOutput } = setup(input); + const { sourceOutput: rerunOutput } = setup(initialOutput); + + expect(initialOutput).toContain('library MathHelpers {'); + expect(initialOutput).toContain('function isEven(int value) internal'); + expect(initialOutput).toContain('function checkParity(int value) internal'); + expect(rerunOutput).toEqual(initialOutput); + }); }); }); diff --git a/packages/cashc/test/ast/Location.test.ts b/packages/cashc/test/ast/Location.test.ts index 4f7d9418..e247559a 100644 --- a/packages/cashc/test/ast/Location.test.ts +++ b/packages/cashc/test/ast/Location.test.ts @@ -12,7 +12,7 @@ describe('Location', () => { const f = ast.contract.functions[0]; expect(f.location).toBeDefined(); - expect((f.location).text(code)).toEqual('function hello(sig s, pubkey pk) {\n require(checkSig(s, pk));\n }'); + expect((f.location).text(code)).toEqual('function hello(sig s, pubkey pk) public {\n require(checkSig(s, pk));\n }'); }); const wrap = (code: string): string => { diff --git a/packages/cashc/test/compiler/CircularFunctionDependencyError/direct_recursive_function.cash b/packages/cashc/test/compiler/CircularFunctionDependencyError/direct_recursive_function.cash new file mode 100644 index 00000000..18843482 --- /dev/null +++ b/packages/cashc/test/compiler/CircularFunctionDependencyError/direct_recursive_function.cash @@ -0,0 +1,9 @@ +contract Test() { + function spend(int value) public { + require(loop(value)); + } + + function loop(int value) internal { + require(loop(value)); + } +} diff --git a/packages/cashc/test/compiler/CircularFunctionDependencyError/mutual_recursive_functions.cash b/packages/cashc/test/compiler/CircularFunctionDependencyError/mutual_recursive_functions.cash new file mode 100644 index 00000000..9bd54d6a --- /dev/null +++ b/packages/cashc/test/compiler/CircularFunctionDependencyError/mutual_recursive_functions.cash @@ -0,0 +1,13 @@ +contract Test() { + function spend(int value) public { + require(first(value)); + } + + function first(int value) internal { + require(second(value)); + } + + function second(int value) internal { + require(first(value)); + } +} diff --git a/packages/cashc/test/compiler/EmptyContractError/internal_only_functions.cash b/packages/cashc/test/compiler/EmptyContractError/internal_only_functions.cash new file mode 100644 index 00000000..a8f4aee4 --- /dev/null +++ b/packages/cashc/test/compiler/EmptyContractError/internal_only_functions.cash @@ -0,0 +1,5 @@ +contract Test() { + function only() internal { + require(true); + } +} diff --git a/packages/cashc/test/compiler/InvokedFunctionContractParameterReferenceError/invoked_function_uses_contract_parameter.cash b/packages/cashc/test/compiler/InvokedFunctionContractParameterReferenceError/invoked_function_uses_contract_parameter.cash new file mode 100644 index 00000000..d8e29f30 --- /dev/null +++ b/packages/cashc/test/compiler/InvokedFunctionContractParameterReferenceError/invoked_function_uses_contract_parameter.cash @@ -0,0 +1,9 @@ +contract Test(int threshold) { + function spend(int value) public { + require(isAboveThreshold(value)); + } + + function isAboveThreshold(int value) internal { + require(value > threshold); + } +} diff --git a/packages/cashc/test/compiler/InvokedFunctionContractParameterReferenceError/transitive_invoked_function_uses_contract_parameter.cash b/packages/cashc/test/compiler/InvokedFunctionContractParameterReferenceError/transitive_invoked_function_uses_contract_parameter.cash new file mode 100644 index 00000000..db0a4747 --- /dev/null +++ b/packages/cashc/test/compiler/InvokedFunctionContractParameterReferenceError/transitive_invoked_function_uses_contract_parameter.cash @@ -0,0 +1,13 @@ +contract Test(int requiredValue) { + function spend(int value) public { + require(wrapper(value)); + } + + function wrapper(int value) internal { + require(matchesRequiredValue(value)); + } + + function matchesRequiredValue(int value) internal { + require(value == requiredValue); + } +} diff --git a/packages/cashc/test/compiler/InvokedFunctionSignatureCheckError/signature_check_in_invoked_function.cash b/packages/cashc/test/compiler/InvokedFunctionSignatureCheckError/signature_check_in_invoked_function.cash new file mode 100644 index 00000000..5eabc70b --- /dev/null +++ b/packages/cashc/test/compiler/InvokedFunctionSignatureCheckError/signature_check_in_invoked_function.cash @@ -0,0 +1,9 @@ +contract Test() { + function hello(sig s, pubkey pk) public { + require(world(s, pk)); + } + + function world(sig s, pubkey pk) internal { + require(checkSig(s, pk)); + } +} diff --git a/packages/cashc/test/compiler/InvokedFunctionSignatureCheckError/transitive_signature_check_in_invoked_function.cash b/packages/cashc/test/compiler/InvokedFunctionSignatureCheckError/transitive_signature_check_in_invoked_function.cash new file mode 100644 index 00000000..9168e9cd --- /dev/null +++ b/packages/cashc/test/compiler/InvokedFunctionSignatureCheckError/transitive_signature_check_in_invoked_function.cash @@ -0,0 +1,13 @@ +contract Test(pubkey owner) { + function spend(sig ownerSig) public { + require(wrapper(ownerSig)); + } + + function wrapper(sig ownerSig) internal { + require(checkOwner(ownerSig)); + } + + function checkOwner(sig ownerSig) internal { + require(checkSig(ownerSig, owner)); + } +} diff --git a/packages/cashc/test/compiler/RedefinitionError/function_with_global_function_name.cash b/packages/cashc/test/compiler/RedefinitionError/function_with_global_function_name.cash new file mode 100644 index 00000000..92f259af --- /dev/null +++ b/packages/cashc/test/compiler/RedefinitionError/function_with_global_function_name.cash @@ -0,0 +1,9 @@ +contract Test() { + function sha256(int value) { + require(value == 1); + } + + function spend() { + require(true); + } +} diff --git a/packages/cashc/test/compiler/compiler.test.ts b/packages/cashc/test/compiler/compiler.test.ts index cad643de..646a76ce 100644 --- a/packages/cashc/test/compiler/compiler.test.ts +++ b/packages/cashc/test/compiler/compiler.test.ts @@ -8,10 +8,21 @@ import { URL } from 'url'; import { getSubdirectories, readCashFiles } from '../test-utils.js'; import * as Errors from '../../src/Errors.js'; -import { compileString } from '../../src/index.js'; +import { compileString, parseCode } from '../../src/index.js'; +import { FunctionVisibility } from '../../src/ast/Globals.js'; describe('Compiler', () => { describe('Successful compilation', () => { + let warnSpy: ReturnType; + + beforeEach(() => { + warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => { }); + }); + + afterEach(() => { + warnSpy.mockRestore(); + }); + readCashFiles(new URL('../valid-contract-files', import.meta.url)).forEach((file) => { it(`${file.fn} should succeed`, () => { expect(() => compileString(file.contents)).not.toThrow(); @@ -20,6 +31,16 @@ describe('Compiler', () => { }); describe('Compilation errors', () => { + let warnSpy: ReturnType; + + beforeEach(() => { + warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => { }); + }); + + afterEach(() => { + warnSpy.mockRestore(); + }); + const errorTypes = getSubdirectories(new URL('.', import.meta.url)); errorTypes.forEach((errorType) => { @@ -37,4 +58,496 @@ describe('Compiler', () => { }); }); }); + + describe('Function visibility', () => { + it('should default omitted visibility to public', () => { + const ast = parseCode(` +contract Test() { + function spend() { + require(true); + } +} +`); + + expect(ast.contract.functions[0]?.visibility).toBe(FunctionVisibility.PUBLIC); + }); + + it('should parse explicit visibility with newlines and comments', () => { + const ast = parseCode(` +contract Test() { + function spend( + int value + ) + /* public entrypoint */ + public { + require(validate(value)); + } + + function validate( + int value + ) + // internal helper + internal { + require(value == 1); + } +} +`); + + expect(ast.contract.functions.map((func) => func.visibility)).toEqual([ + FunctionVisibility.PUBLIC, + FunctionVisibility.INTERNAL, + ]); + }); + + it('should not infer visibility from underscores in names', () => { + const artifact = compileString(` +contract Test() { + function spend_value() { + require(helper_value()); + } + + function helper_value() public { + require(true); + } +} +`); + + expect(artifact.abi.map((func) => func.name)).toEqual(['spend_value', 'helper_value']); + }); + + it('should reject duplicated visibility markers', () => { + expect(() => compileString(` +contract Test() { + function spend() public internal { + require(true); + } +} +`)).toThrow(Errors.ParseError); + }); + + it('should reject visibility before the function keyword', () => { + expect(() => compileString(` +contract Test() { + public function spend() { + require(true); + } +} +`)).toThrow(Errors.ParseError); + }); + + it('should warn when visibility is omitted', () => { + const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => { }); + + compileString(` +contract Test() { + function spend() { + require(true); + } +} +`); + + expect(warnSpy).toHaveBeenCalledWith( + 'Warning: 1 function(s) omit visibility and default to public: spend (Line 3, Column 11).', + ); + + warnSpy.mockRestore(); + }); + + it('should not warn when visibility is explicit', () => { + const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => { }); + + compileString(` +contract Test() { + function spend() public { + require(validate()); + } + + function validate() internal { + require(true); + } +} +`); + + expect(warnSpy).not.toHaveBeenCalled(); + warnSpy.mockRestore(); + }); + }); + + describe('Libraries', () => { + it('should parse top-level libraries as non-spendable helper containers', () => { + const ast = parseCode(` +library MathHelpers { + function isEven(int value) internal { + require(value % 2 == 0); + } +} +`); + + expect(ast.contract.kind).toBe('library'); + expect(ast.contract.parameters).toEqual([]); + expect(ast.contract.functions[0]?.visibility).toBe(FunctionVisibility.INTERNAL); + }); + + it('should reject compiling a top-level library to an artifact', () => { + expect(() => compileString(` +library MathHelpers { + function isEven(int value) internal { + require(value % 2 == 0); + } +} +`)).toThrow(Errors.NonSpendableCompilationError); + }); + + it('should reject public functions inside a library', () => { + expect(() => compileString(` +library BadHelpers { + function isEven(int value) public { + require(value % 2 == 0); + } +} +`)).toThrow(Errors.ParseError); + }); + + it('should require internal functions inside a library', () => { + expect(() => compileString(` +library BadHelpers { + function isEven(int value) { + require(value % 2 == 0); + } +} +`)).toThrow(Errors.ParseError); + }); + + it('should compile contracts that import helper libraries', () => { + const artifact = compileString(` +import "./math.cash" as Math; + +contract UsesLibrary() { + function spend(int value) public { + require(Math.isEven(value)); + } +} +`, { + sourcePath: '/contracts/main.cash', + resolveImport: (specifier) => { + expect(specifier).toBe('./math.cash'); + return ` +library MathHelpers { + function isEven(int value) internal { + require(value % 2 == 0); + } +} +`; + }, + }); + + expect(artifact.abi).toEqual([ + { name: 'spend', inputs: [{ name: 'value', type: 'int' }] }, + ]); + }); + + it('should reject importing a spendable contract as a helper library', () => { + expect(() => compileString(` +import "./other.cash" as Other; + +contract UsesLibrary() { + function spend(int value) public { + require(Other.isEven(value)); + } +} +`, { + sourcePath: '/contracts/main.cash', + resolveImport: () => ` +contract Other() { + function isEven(int value) public { + require(value % 2 == 0); + } +} +`, + })).toThrow(Errors.InvalidLibraryImportError); + }); + + it('should reject imported libraries with omitted visibility', () => { + expect(() => compileString(` +import "./math.cash" as Math; + +contract UsesLibrary() { + function spend(int value) public { + require(Math.isEven(value)); + } +} +`, { + sourcePath: '/contracts/main.cash', + resolveImport: () => ` +library MathHelpers { + function isEven(int value) { + require(value % 2 == 0); + } +} +`, + })).toThrow(Errors.InvalidLibraryImportError); + }); + + it('should reject imported libraries that reference non-library local functions', () => { + expect(() => compileString(` +import "./math.cash" as Math; + +contract UsesLibrary() { + function spend(int value) public { + require(Math.isEven(value)); + } +} +`, { + sourcePath: '/contracts/main.cash', + resolveImport: () => ` +library MathHelpers { + function isEven(int value) internal { + require(check(value)); + } +} +`, + })).toThrow(Errors.InvalidLibraryImportError); + }); + + it('should support transitive library imports', () => { + const artifact = compileString(` +import "./math.cash" as Math; + +contract UsesLibrary() { + function spend(int value) public { + require(Math.isEven(value)); + } +} +`, { + sourcePath: '/contracts/main.cash', + resolveImport: (specifier) => { + if (specifier === './math.cash') { + return { + path: '/contracts/math.cash', + source: ` +import "./core.cash" as Core; + +library MathHelpers { + function isEven(int value) internal { + require(Core.isZero(value % 2)); + } +} +`, + }; + } + + return { + path: '/contracts/core.cash', + source: ` +library CoreHelpers { + function isZero(int value) internal { + require(value == 0); + } +} +`, + }; + }, + }); + + expect(artifact.abi).toEqual([ + { name: 'spend', inputs: [{ name: 'value', type: 'int' }] }, + ]); + }); + + it('should reject circular transitive library imports', () => { + expect(() => compileString(` +import "./math.cash" as Math; + +contract UsesLibrary() { + function spend(int value) public { + require(Math.isEven(value)); + } +} +`, { + sourcePath: '/contracts/main.cash', + resolveImport: (specifier) => { + if (specifier === './math.cash') { + return { + path: '/contracts/math.cash', + source: ` +import "./core.cash" as Core; + +library MathHelpers { + function isEven(int value) internal { + require(Core.isZero(value % 2)); + } +} +`, + }; + } + + return { + path: '/contracts/core.cash', + source: ` +import "./math.cash" as Math; + +library CoreHelpers { + function isZero(int value) internal { + require(Math.isEven(value)); + } +} +`, + }; + }, + })).toThrow(Errors.InvalidLibraryImportError); + }); + + it('should reject duplicate import aliases', () => { + expect(() => compileString(` +import "./math.cash" as Helpers; +import "./bits.cash" as Helpers; + +contract UsesLibraries() { + function spend(int value) public { + require(Helpers.isEven(value)); + } +} +`, { + sourcePath: '/contracts/main.cash', + resolveImport: () => ` +library Helpers { + function isEven(int value) internal { + require(value % 2 == 0); + } +} +`, + })).toThrow(Errors.InvalidImportDirectiveError); + }); + + it('should reject calls to missing imported library functions', () => { + expect(() => compileString(` +import "./math.cash" as Math; + +contract UsesLibrary() { + function spend(int value) public { + require(Math.isOdd(value)); + } +} +`, { + sourcePath: '/contracts/main.cash', + resolveImport: () => ` +library MathHelpers { + function isEven(int value) internal { + require(value % 2 == 0); + } +} +`, + })).toThrow(Errors.InvalidImportDirectiveError); + }); + + it('should reject imported libraries that attempt namespaced external helper calls', () => { + expect(() => compileString(` +import "./math.cash" as Math; + +contract UsesLibrary() { + function spend(int value) public { + require(Math.isEven(value)); + } +} +`, { + sourcePath: '/contracts/main.cash', + resolveImport: () => ` +library MathHelpers { + function isEven(int value) internal { + require(Other.check(value)); + } +} +`, + })).toThrow(Errors.InvalidLibraryImportError); + }); + + it('should not rewrite imported helper references inside comments or strings', () => { + const artifact = compileString(` +import "./math.cash" as Math; + +contract UsesLibrary() { + function spend(int value) public { + console.log("Math.isEven(value) should stay literal"); + // Math.isEven(value) should stay in the comment too + require(Math.isEven(value)); + } +} +`, { + sourcePath: '/contracts/main.cash', + resolveImport: () => ` +library MathHelpers { + function isEven(int value) internal { + require(value % 2 == 0); + } +} +`, + }); + + expect(artifact.abi).toEqual([ + { name: 'spend', inputs: [{ name: 'value', type: 'int' }] }, + ]); + }); + + it('should canonicalise shared transitive libraries by resolved file identity', () => { + const artifact = compileString(` +import "./math.cash" as Math; +import "./bits.cash" as Bits; + +contract UsesLibraries() { + function spend(int value) public { + require(Math.isEven(value)); + require(Bits.isOdd(value + 1)); + } +} +`, { + sourcePath: '/contracts/main.cash', + resolveImport: (specifier) => { + if (specifier === './math.cash') { + return { + path: '/contracts/math.cash', + source: ` +import "./shared.cash" as Shared; + +library MathHelpers { + function isEven(int value) internal { + require(Shared.isParity(value, 0)); + } +} +`, + }; + } + + if (specifier === './bits.cash') { + return { + path: '/contracts/bits.cash', + source: ` +import "./shared.cash" as Shared; + +library BitHelpers { + function isOdd(int value) internal { + require(Shared.isParity(value, 1)); + } +} +`, + }; + } + + return { + path: '/contracts/shared.cash', + source: ` +library SharedHelpers { + function isParity(int value, int parity) internal { + require(value % 2 == parity); + } +} +`, + }; + }, + }); + + expect(artifact.abi).toEqual([ + { name: 'spend', inputs: [{ name: 'value', type: 'int' }] }, + ]); + }); + }); }); diff --git a/packages/cashc/test/generation/generation.test.ts b/packages/cashc/test/generation/generation.test.ts index 4e416881..72695063 100644 --- a/packages/cashc/test/generation/generation.test.ts +++ b/packages/cashc/test/generation/generation.test.ts @@ -4,14 +4,507 @@ */ import { URL } from 'url'; -import { compileFile } from '../../src/index.js'; +import { compileFile, compileString } from '../../src/index.js'; import { fixtures } from './fixtures.js'; +import { Artifact, LogData, LogEntry, RequireStatement, StackItem } from '@cashscript/utils'; + +const stripFrameBytecodeFromLogData = (entry: LogData): LogData => { + if (typeof entry === 'string') return entry; + + const stackItem: StackItem = { + ip: entry.ip, + stackIndex: entry.stackIndex, + type: entry.type, + ...(entry.transformations ? { transformations: entry.transformations } : {}), + }; + + return stackItem; +}; + +const stripFrameMetadataFromArtifact = (artifact: Artifact): Artifact => { + if (!artifact.debug) { + return artifact; + } + + const debugWithoutFrames = Object.fromEntries( + Object.entries(artifact.debug).filter(([key]) => key !== 'frames'), + ) as typeof artifact.debug; + const strippedDebug = { + ...debugWithoutFrames, + logs: artifact.debug.logs.map((log): LogEntry => ({ + ip: log.ip, + line: log.line, + data: log.data.map(stripFrameBytecodeFromLogData), + })), + requires: artifact.debug.requires.map((requireStatement): RequireStatement => ({ + ip: requireStatement.ip, + line: requireStatement.line, + ...(requireStatement.message ? { message: requireStatement.message } : {}), + })), + }; + + return { + ...artifact, + debug: strippedDebug, + }; +}; + +const stripExtendedDebugMetadata = (artifact: Artifact): Artifact => { + const strippedArtifact = stripFrameMetadataFromArtifact(artifact); + return { + ...strippedArtifact, + ...(strippedArtifact.debug ? { + debug: { + ...strippedArtifact.debug, + bytecode: strippedArtifact.debug.bytecode, + sourceMap: strippedArtifact.debug.sourceMap, + logs: strippedArtifact.debug.logs, + requires: strippedArtifact.debug.requires, + }, + } : {}), + }; +}; describe('Code generation & target code optimisation', () => { fixtures.forEach((fixture) => { it(`should compile ${fixture.fn} to correct Script and artifact`, () => { const artifact = compileFile(new URL(`../valid-contract-files/${fixture.fn}`, import.meta.url), fixture.compilerOptions); - expect(artifact).toEqual({ ...fixture.artifact, updatedAt: expect.any(String) }); + expect(stripExtendedDebugMetadata(artifact)).toEqual({ ...fixture.artifact, updatedAt: expect.any(String) }); + }); + }); + + it('should hide internal functions from the ABI and emit BCH function opcodes', () => { + const artifact = compileString(` +contract InternalFunctions() { + function spend(int x) public { + require(isEven(x)); + } + + function isEven(int value) internal { + require(value % 2 == 0); + } +} +`); + + expect(artifact.abi).toEqual([ + { name: 'spend', inputs: [{ name: 'x', type: 'int' }] }, + ]); + expect(artifact.bytecode).toContain('OP_DEFINE'); + expect(artifact.bytecode).toContain('OP_INVOKE'); + }); + + it('should only emit OP_DEFINE for reachable internal functions', () => { + const artifact = compileString(` +contract ReachableHelpers() { + function spend(int x) public { + require(doubleCheck(x)); + } + + function fallback(int x) public { + require(isEven(x)); + } + + function doubleCheck(int value) internal { + require(isEven(value)); + } + + function isEven(int value) internal { + require(value % 2 == 0); + } + + function unused(int value) internal { + require(value == 42); + } +} +`); + + expect(artifact.abi).toEqual([ + { name: 'spend', inputs: [{ name: 'x', type: 'int' }] }, + { name: 'fallback', inputs: [{ name: 'x', type: 'int' }] }, + ]); + expect(artifact.bytecode.match(/OP_DEFINE/g)).toHaveLength(2); + expect(artifact.bytecode.match(/OP_INVOKE/g)).toHaveLength(2); + }); + + it('should not emit BCH function opcodes for unused internal functions', () => { + const artifact = compileString(` +contract UnusedHelpers() { + function spend(int x) public { + require(x == 7); + } + + function helper(int value) internal { + require(value == 7); + } +} +`); + + expect(artifact.abi).toEqual([ + { name: 'spend', inputs: [{ name: 'x', type: 'int' }] }, + ]); + expect(artifact.bytecode).not.toContain('OP_DEFINE'); + expect(artifact.bytecode).not.toContain('OP_INVOKE'); + }); + + it('should ignore internal-only call chains that are unreachable from public entrypoints', () => { + const artifact = compileString(` +contract DeadHelperChain() { + function spend(int x) public { + require(x == 7); + } + + function helperA(int value) internal { + require(helperB(value)); + } + + function helperB(int value) internal { + require(value == 7); + } +} +`); + + expect(artifact.abi).toEqual([ + { name: 'spend', inputs: [{ name: 'x', type: 'int' }] }, + ]); + expect(artifact.bytecode).not.toContain('OP_DEFINE'); + expect(artifact.bytecode).not.toContain('OP_INVOKE'); + expect(artifact.compiler.target).toBeUndefined(); + }); + + it('should support invoking a public function from another public function', () => { + const artifact = compileString(` +contract PublicCalls() { + function spend(int x) public { + require(validate(x)); + } + + function validate(int value) public { + require(value == 7); + } +} +`); + + expect(artifact.abi).toEqual([ + { name: 'spend', inputs: [{ name: 'x', type: 'int' }] }, + { name: 'validate', inputs: [{ name: 'value', type: 'int' }] }, + ]); + expect(artifact.bytecode.match(/OP_DEFINE/g)).toHaveLength(1); + expect(artifact.bytecode.match(/OP_INVOKE/g)).toHaveLength(2); + expect(artifact.compiler.target).toBe('BCH_2026_05'); + }); + + it('should treat function names literally rather than inferring visibility from underscores', () => { + const artifact = compileString(` +contract LiteralNames() { + function spend(int x) public { + require(helper_check(x)); + } + + function helper_check(int value) public { + require(value == 11); + } +} +`); + + expect(artifact.abi).toEqual([ + { name: 'spend', inputs: [{ name: 'x', type: 'int' }] }, + { name: 'helper_check', inputs: [{ name: 'value', type: 'int' }] }, + ]); + expect(artifact.bytecode.match(/OP_DEFINE/g)).toHaveLength(1); + expect(artifact.bytecode.match(/OP_INVOKE/g)).toHaveLength(2); + }); + + it('should reuse a shared frame when an internal helper invokes a public function', () => { + const artifact = compileString(` +contract InternalCallsPublic() { + function spend(int x) public { + require(route(x)); + } + + function route(int value) internal { + require(validate(value)); + } + + function validate(int value) public { + require(value == 11); + } +} +`); + + expect(artifact.abi).toEqual([ + { name: 'spend', inputs: [{ name: 'x', type: 'int' }] }, + { name: 'validate', inputs: [{ name: 'value', type: 'int' }] }, + ]); + expect(artifact.bytecode.match(/OP_DEFINE/g)).toHaveLength(2); + expect(artifact.bytecode.match(/OP_INVOKE/g)).toHaveLength(2); + expect(artifact.compiler.target).toBe('BCH_2026_05'); + }); + + it('should reject explicit pre-2026 targets when internal-function opcodes are required', () => { + expect(() => compileString(` +contract ExplicitLegacyTarget() { + function spend(int x) public { + require(isTen(x)); + } + + function isTen(int value) internal { + require(value == 10); + } +} +`, { target: 'BCH_2025_05' })).toThrow(/at least BCH_2026_05 is required/); + }); + + it('should allow BCH_SPEC when internal-function opcodes are required', () => { + const artifact = compileString(` +contract SpecTarget() { + function spend(int x) public { + require(isTen(x)); + } + + function isTen(int value) internal { + require(value == 10); + } +} +`, { target: 'BCH_SPEC' }); + + expect(artifact.compiler.target).toBe('BCH_SPEC'); + }); + + it('should support explicit internal visibility without name decoration', () => { + const artifact = compileString(` +contract ExplicitVisibility() { + function spend(int x) public { + require(checkValue(x)); + } + + function checkValue(int value) internal { + require(value == 12); + } +} +`); + + expect(artifact.abi).toEqual([ + { name: 'spend', inputs: [{ name: 'x', type: 'int' }] }, + ]); + expect(artifact.bytecode).toContain('OP_DEFINE'); + expect(artifact.bytecode).toContain('OP_INVOKE'); + }); + + it('should annotate debug metadata with frame bytecode and require locations', () => { + const artifact = compileString(` +contract DebugMetadata() { + function spend(int x) public { + require(validate(x)); + } + + function validate(int value) internal { + console.log('value:', value); + require(value == 12, 'value must equal 12'); + } +} +`); + + expect(artifact.debug?.logs[0]?.frameBytecode).toBeDefined(); + expect((artifact.debug?.logs[0]?.data[1] as StackItem | undefined)?.frameBytecode).toBeDefined(); + const publicRequire = artifact.debug?.requires.find((requireStatement) => requireStatement.message === undefined); + const internalRequire = artifact.debug?.requires.find((requireStatement) => ( + requireStatement.message === 'value must equal 12' + )); + + expect(publicRequire?.frameBytecode).toBeDefined(); + expect(publicRequire?.location).toEqual({ + start: { line: 4, column: 4 }, + end: { line: 4, column: 25 }, }); + expect(internalRequire?.frameBytecode).toBeDefined(); + expect(internalRequire?.location).toEqual({ + start: { line: 9, column: 4 }, + end: { line: 9, column: 48 }, + }); + }); + + it('should lower imported library helpers to BCH function opcodes without exposing them in the ABI', () => { + const artifact = compileString(` +import "./math.cash" as Math; + +contract UsesLibrary() { + function spend(int value) public { + require(Math.isEven(value)); + } +} +`, { + sourcePath: '/contracts/main.cash', + resolveImport: () => ` +library MathHelpers { + function isEven(int value) internal { + require(value % 2 == 0); + } +} +`, + }); + + expect(artifact.abi).toEqual([ + { name: 'spend', inputs: [{ name: 'value', type: 'int' }] }, + ]); + expect(artifact.bytecode).toContain('OP_DEFINE'); + expect(artifact.bytecode).toContain('OP_INVOKE'); + expect(artifact.compiler.target).toBe('BCH_2026_05'); + }); + + it('should lower transitive imported library helpers without duplicating shared leaves', () => { + const artifact = compileString(` +import "./math.cash" as Math; +import "./bits.cash" as Bits; + +contract UsesLibraries() { + function spend(int value) public { + require(Math.isEven(value)); + require(Bits.isOdd(value + 1)); + } +} +`, { + sourcePath: '/contracts/main.cash', + resolveImport: (specifier) => { + if (specifier === './math.cash') { + return { + path: '/contracts/math.cash', + source: ` +import "./shared.cash" as Shared; + +library MathHelpers { + function isEven(int value) internal { + require(Shared.isParity(value, 0)); + } +} +`, + }; + } + + if (specifier === './bits.cash') { + return { + path: '/contracts/bits.cash', + source: ` +import "./shared.cash" as Shared; + +library BitHelpers { + function isOdd(int value) internal { + require(Shared.isParity(value, 1)); + } +} +`, + }; + } + + return { + path: '/contracts/shared.cash', + source: ` +library SharedHelpers { + function isParity(int value, int parity) internal { + require(value % 2 == parity); + } +} +`, + }; + }, + }); + + expect(artifact.bytecode.match(/OP_DEFINE/g)).toHaveLength(3); + expect(artifact.bytecode.match(/OP_INVOKE/g)).toHaveLength(2); + }); + + it('should reject contracts whose helper frames would have ambiguous identical bytecode', () => { + expect(() => compileString(` +contract AmbiguousHelpers() { + function spendA() public { + require(checkA()); + } + + function spendB() public { + require(checkB()); + } + + function checkA() internal { + require(true); + } + + function checkB() internal { + require(true); + } +} +`)).toThrow(/identical helper bytecode/); + }); + + it('should support multiple imported libraries with overlapping helper names', () => { + const artifact = compileString(` +import "./math.cash" as Math; +import "./bits.cash" as Bits; + +contract UsesLibraries() { + function spend(int value) public { + require(Math.isEven(value)); + require(Bits.isEven(value)); + } +} +`, { + sourcePath: '/contracts/main.cash', + resolveImport: (specifier) => { + if (specifier === './math.cash') { + return ` +library MathHelpers { + function isEven(int value) internal { + require(value % 2 == 0); + } +} +`; + } + + return ` +library BitHelpers { + function isEven(int value) internal { + require((value % 4) == 0 || (value % 4) == 2); + } +} +`; + }, + }); + + expect(artifact.abi).toEqual([ + { name: 'spend', inputs: [{ name: 'value', type: 'int' }] }, + ]); + expect(artifact.bytecode.match(/OP_DEFINE/g)).toHaveLength(2); + expect(artifact.bytecode.match(/OP_INVOKE/g)).toHaveLength(2); + expect(artifact.compiler.target).toBe('BCH_2026_05'); + }); + + it('should emit only reachable imported helper call chains', () => { + const artifact = compileString(` +import "./math.cash" as Math; + +contract UsesLibrary() { + function spend(int value) public { + require(Math.isEven(value)); + } +} +`, { + sourcePath: '/contracts/main.cash', + resolveImport: () => ` +library MathHelpers { + function isEven(int value) internal { + require(checkParity(value)); + } + + function checkParity(int value) internal { + require(value % 2 == 0); + } + + function unused(int value) internal { + require(value == 123); + } +} +`, + }); + + expect(artifact.bytecode.match(/OP_DEFINE/g)).toHaveLength(2); + expect(artifact.bytecode.match(/OP_INVOKE/g)).toHaveLength(1); }); }); diff --git a/packages/cashc/test/valid-contract-files/2_of_3_multisig.cash b/packages/cashc/test/valid-contract-files/2_of_3_multisig.cash index abb5bd28..b4b181ee 100644 --- a/packages/cashc/test/valid-contract-files/2_of_3_multisig.cash +++ b/packages/cashc/test/valid-contract-files/2_of_3_multisig.cash @@ -1,5 +1,5 @@ contract MultiSig(pubkey pk1, pubkey pk2, pubkey pk3) { - function spend(sig s1, sig s2) { + function spend(sig s1, sig s2) public { require(checkMultiSig([s1, s2], [pk1, pk2, pk3])); } } diff --git a/packages/cashc/test/valid-contract-files/announcement.cash b/packages/cashc/test/valid-contract-files/announcement.cash index 0226b2e7..a35baf58 100644 --- a/packages/cashc/test/valid-contract-files/announcement.cash +++ b/packages/cashc/test/valid-contract-files/announcement.cash @@ -5,7 +5,7 @@ pragma cashscript >=0.8.0; * remainder of contract funds back to the contract. */ contract Announcement() { - function announce() { + function announce() public { // Create the memo.cash announcement output bytes announcement = new LockingBytecodeNullData([ 0x6d02, diff --git a/packages/cashc/test/valid-contract-files/bigint.cash b/packages/cashc/test/valid-contract-files/bigint.cash index e236f5dd..5e86e503 100644 --- a/packages/cashc/test/valid-contract-files/bigint.cash +++ b/packages/cashc/test/valid-contract-files/bigint.cash @@ -1,5 +1,5 @@ contract BigInt() { - function proofOfBigInt(int x, int y) { + function proofOfBigInt(int x, int y) public { int maxInt32PlusOne = 2147483648; require(x >= maxInt32PlusOne); require(x * y >= maxInt32PlusOne); diff --git a/packages/cashc/test/valid-contract-files/bitshift.cash b/packages/cashc/test/valid-contract-files/bitshift.cash index bce4ad3a..964f9d2a 100644 --- a/packages/cashc/test/valid-contract-files/bitshift.cash +++ b/packages/cashc/test/valid-contract-files/bitshift.cash @@ -1,5 +1,5 @@ contract Bitshift() { - function spend() { + function spend() public { bytes8 x = 0x1122334455667788; bytes8 y = x << 4 >> 4; diff --git a/packages/cashc/test/valid-contract-files/bitwise.cash b/packages/cashc/test/valid-contract-files/bitwise.cash index 3c1f8a30..bfe62642 100644 --- a/packages/cashc/test/valid-contract-files/bitwise.cash +++ b/packages/cashc/test/valid-contract-files/bitwise.cash @@ -1,5 +1,5 @@ contract Test(bytes8 x, bytes8 y) { - function hello() { + function hello() public { require((x | y) == y); } } diff --git a/packages/cashc/test/valid-contract-files/bounded_bytes.cash b/packages/cashc/test/valid-contract-files/bounded_bytes.cash index 2fc2e8b8..3cb6c8a5 100644 --- a/packages/cashc/test/valid-contract-files/bounded_bytes.cash +++ b/packages/cashc/test/valid-contract-files/bounded_bytes.cash @@ -1,5 +1,5 @@ contract BoundedBytes() { - function spend(bytes4 b, int i) { + function spend(bytes4 b, int i) public { require(b == toPaddedBytes(i, 4)); } } diff --git a/packages/cashc/test/valid-contract-files/bytes1_equals_byte.cash b/packages/cashc/test/valid-contract-files/bytes1_equals_byte.cash index 0dc54ca5..c5ecbe4b 100644 --- a/packages/cashc/test/valid-contract-files/bytes1_equals_byte.cash +++ b/packages/cashc/test/valid-contract-files/bytes1_equals_byte.cash @@ -1,5 +1,5 @@ contract Bytes1EqualsByte() { - function hello(int a, byte b) { + function hello(int a, byte b) public { bytes1 c = toPaddedBytes(a, 1); require(b == c); } diff --git a/packages/cashc/test/valid-contract-files/cast_hash_checksig.cash b/packages/cashc/test/valid-contract-files/cast_hash_checksig.cash index e1bc81b5..ec8f4f27 100644 --- a/packages/cashc/test/valid-contract-files/cast_hash_checksig.cash +++ b/packages/cashc/test/valid-contract-files/cast_hash_checksig.cash @@ -1,5 +1,5 @@ contract CastHashChecksig() { - function hello(pubkey pk, sig s) { + function hello(pubkey pk, sig s) public { require((ripemd160(bytes(pk)) == hash160(0x) == !true)); require(checkSig(s, pk)); } diff --git a/packages/cashc/test/valid-contract-files/comments.cash b/packages/cashc/test/valid-contract-files/comments.cash index 8ea8705a..9240ab89 100644 --- a/packages/cashc/test/valid-contract-files/comments.cash +++ b/packages/cashc/test/valid-contract-files/comments.cash @@ -5,7 +5,7 @@ contract Test(int x) { // Line comments are a thing - function hello(sig s, pubkey pk) { + function hello(sig s, pubkey pk) public { int i = 400 + x; bytes b = 0x07364897987fe87 + bytes(x); diff --git a/packages/cashc/test/valid-contract-files/complex_loop.cash b/packages/cashc/test/valid-contract-files/complex_loop.cash index 338d7525..1929b26a 100644 --- a/packages/cashc/test/valid-contract-files/complex_loop.cash +++ b/packages/cashc/test/valid-contract-files/complex_loop.cash @@ -1,5 +1,5 @@ contract Loopy() { - function doLoop() { + function doLoop() public { int inputSum = 0; int outputSum = 0; int tokenCount = 0; diff --git a/packages/cashc/test/valid-contract-files/correct_pragma.cash b/packages/cashc/test/valid-contract-files/correct_pragma.cash index 58552541..1cd385ab 100644 --- a/packages/cashc/test/valid-contract-files/correct_pragma.cash +++ b/packages/cashc/test/valid-contract-files/correct_pragma.cash @@ -1,11 +1,11 @@ pragma cashscript >=0.8.0; contract Test() { - function hello(sig s, pubkey pk) { + function hello(sig s, pubkey pk) public { require(checkSig(s, pk)); } - function world(int a) { + function world(int a) public { require(a + 5 == 10); } } diff --git a/packages/cashc/test/valid-contract-files/covenant.cash b/packages/cashc/test/valid-contract-files/covenant.cash index 7e18a30e..0ee5afbe 100644 --- a/packages/cashc/test/valid-contract-files/covenant.cash +++ b/packages/cashc/test/valid-contract-files/covenant.cash @@ -1,5 +1,5 @@ contract Covenant(int requiredVersion) { - function spend() { + function spend() public { require(tx.version == requiredVersion); require(this.activeBytecode == 0x00); } diff --git a/packages/cashc/test/valid-contract-files/covenant_all_fields.cash b/packages/cashc/test/valid-contract-files/covenant_all_fields.cash index 442c1ff3..69f901b4 100644 --- a/packages/cashc/test/valid-contract-files/covenant_all_fields.cash +++ b/packages/cashc/test/valid-contract-files/covenant_all_fields.cash @@ -1,5 +1,5 @@ contract Covenant() { - function spend() { + function spend() public { require(tx.version == 2); require(tx.locktime == 0); require(tx.inputs.length == 1); diff --git a/packages/cashc/test/valid-contract-files/date_literal.cash b/packages/cashc/test/valid-contract-files/date_literal.cash index 05d1e757..02ebcb19 100644 --- a/packages/cashc/test/valid-contract-files/date_literal.cash +++ b/packages/cashc/test/valid-contract-files/date_literal.cash @@ -1,7 +1,7 @@ pragma cashscript >=0.8.0; contract Test() { - function test() { + function test() public { int d = date("2021-02-17T01:30:00"); //YYYY-MM-DDThh:mm:ss require(d == 0); } diff --git a/packages/cashc/test/valid-contract-files/debug_messages.cash b/packages/cashc/test/valid-contract-files/debug_messages.cash index 92203434..b4724170 100644 --- a/packages/cashc/test/valid-contract-files/debug_messages.cash +++ b/packages/cashc/test/valid-contract-files/debug_messages.cash @@ -1,5 +1,5 @@ contract DebugMessages() { - function spend(int value) { + function spend(int value) public { require(value == 1, "Wrong value passed"); console.log(value, "test"); console.log(value, "test2"); diff --git a/packages/cashc/test/valid-contract-files/deep_replace.cash b/packages/cashc/test/valid-contract-files/deep_replace.cash index a1462b94..6b24b115 100644 --- a/packages/cashc/test/valid-contract-files/deep_replace.cash +++ b/packages/cashc/test/valid-contract-files/deep_replace.cash @@ -1,5 +1,5 @@ contract DeepReplace() { - function hello() { + function hello() public { int a = 1; int b = 2; int c = 3; diff --git a/packages/cashc/test/valid-contract-files/deeply_nested-logs.cash b/packages/cashc/test/valid-contract-files/deeply_nested-logs.cash index d6664825..e6b00cd5 100644 --- a/packages/cashc/test/valid-contract-files/deeply_nested-logs.cash +++ b/packages/cashc/test/valid-contract-files/deeply_nested-logs.cash @@ -6,13 +6,13 @@ contract TransferWithTimeout( int timeout ) { // Require recipient's signature to match - function transfer(sig recipientSig) { + function transfer(sig recipientSig) public { require(checkSig(recipientSig, recipient)); console.log('recipientSig is', recipientSig); } // Require timeout time to be reached and sender's signature to match - function timeout(sig senderSig) { + function timeout(sig senderSig) public { require(checkSig(senderSig, sender)); if (timeout > 0) { if (timeout < 10) { diff --git a/packages/cashc/test/valid-contract-files/deeply_nested.cash b/packages/cashc/test/valid-contract-files/deeply_nested.cash index c5f1e31a..0b63bb32 100644 --- a/packages/cashc/test/valid-contract-files/deeply_nested.cash +++ b/packages/cashc/test/valid-contract-files/deeply_nested.cash @@ -6,12 +6,12 @@ contract TransferWithTimeout( int timeout ) { // Require recipient's signature to match - function transfer(sig recipientSig) { + function transfer(sig recipientSig) public { require(checkSig(recipientSig, recipient)); } // Require timeout time to be reached and sender's signature to match - function timeout(sig senderSig) { + function timeout(sig senderSig) public { require(checkSig(senderSig, sender)); if (timeout > 0) { if (timeout < 10) { diff --git a/packages/cashc/test/valid-contract-files/do_while_loop.cash b/packages/cashc/test/valid-contract-files/do_while_loop.cash index 2cfb3292..60ab9dd8 100644 --- a/packages/cashc/test/valid-contract-files/do_while_loop.cash +++ b/packages/cashc/test/valid-contract-files/do_while_loop.cash @@ -1,5 +1,5 @@ contract Loopy() { - function doLoop() { + function doLoop() public { int i = 0; do { diff --git a/packages/cashc/test/valid-contract-files/do_while_loop_no_introspection.cash b/packages/cashc/test/valid-contract-files/do_while_loop_no_introspection.cash index a11a89b1..b0d2fb6c 100644 --- a/packages/cashc/test/valid-contract-files/do_while_loop_no_introspection.cash +++ b/packages/cashc/test/valid-contract-files/do_while_loop_no_introspection.cash @@ -1,5 +1,5 @@ contract Loopy() { - function doLoop() { + function doLoop() public { int i = 0; int x = 2; diff --git a/packages/cashc/test/valid-contract-files/do_while_loop_no_tokens_in_inputs.cash b/packages/cashc/test/valid-contract-files/do_while_loop_no_tokens_in_inputs.cash index 07290469..1c9a20a2 100644 --- a/packages/cashc/test/valid-contract-files/do_while_loop_no_tokens_in_inputs.cash +++ b/packages/cashc/test/valid-contract-files/do_while_loop_no_tokens_in_inputs.cash @@ -1,7 +1,7 @@ pragma cashscript ^0.13.0; contract Loopy() { - function doLoop() { + function doLoop() public { int inputIndex = 0; // Loop over all inputs (variable length), and make sure that none of them contain tokens diff --git a/packages/cashc/test/valid-contract-files/do_while_loop_require_inside_loop.cash b/packages/cashc/test/valid-contract-files/do_while_loop_require_inside_loop.cash index 6a751cf4..3ddec1cd 100644 --- a/packages/cashc/test/valid-contract-files/do_while_loop_require_inside_loop.cash +++ b/packages/cashc/test/valid-contract-files/do_while_loop_require_inside_loop.cash @@ -1,5 +1,5 @@ contract Loopy() { - function doLoop() { + function doLoop() public { int i = 0; do { diff --git a/packages/cashc/test/valid-contract-files/do_while_no_require.cash b/packages/cashc/test/valid-contract-files/do_while_no_require.cash index 4b4fa097..41b95f38 100644 --- a/packages/cashc/test/valid-contract-files/do_while_no_require.cash +++ b/packages/cashc/test/valid-contract-files/do_while_no_require.cash @@ -1,5 +1,5 @@ contract Loopy() { - function doLoop() { + function doLoop() public { int i = 0; require(i < 1); diff --git a/packages/cashc/test/valid-contract-files/double_split.cash b/packages/cashc/test/valid-contract-files/double_split.cash index b0baa894..4a567557 100644 --- a/packages/cashc/test/valid-contract-files/double_split.cash +++ b/packages/cashc/test/valid-contract-files/double_split.cash @@ -1,5 +1,5 @@ contract DoubleSplit(bytes20 pkh) { - function spend() { + function spend() public { bytes actualPkh = tx.inputs[this.activeInputIndex].lockingBytecode.split(23)[0].split(3)[1]; require(pkh == actualPkh); } diff --git a/packages/cashc/test/valid-contract-files/everything.cash b/packages/cashc/test/valid-contract-files/everything.cash index 63dab05c..a4960c5e 100644 --- a/packages/cashc/test/valid-contract-files/everything.cash +++ b/packages/cashc/test/valid-contract-files/everything.cash @@ -5,7 +5,7 @@ contract Test(int x, string y) { // Line comments are a thing - function hello(sig s, pubkey pk) { + function hello(sig s, pubkey pk) public { int i = 400 + x; bytes b = 0x07364897987fe87 + bytes(y); diff --git a/packages/cashc/test/valid-contract-files/for_loop_basic.cash b/packages/cashc/test/valid-contract-files/for_loop_basic.cash index 27fa9a55..abe13519 100644 --- a/packages/cashc/test/valid-contract-files/for_loop_basic.cash +++ b/packages/cashc/test/valid-contract-files/for_loop_basic.cash @@ -1,5 +1,5 @@ contract ForLoopBasic() { - function spend() { + function spend() public { int sum = 0; for (int i = 0; i < 3; i = i + 1) { diff --git a/packages/cashc/test/valid-contract-files/for_loop_stack_items.cash b/packages/cashc/test/valid-contract-files/for_loop_stack_items.cash index 2ee7946f..decb28fa 100644 --- a/packages/cashc/test/valid-contract-files/for_loop_stack_items.cash +++ b/packages/cashc/test/valid-contract-files/for_loop_stack_items.cash @@ -1,5 +1,5 @@ contract ForLoopBasic() { - function spend() { + function spend() public { int sum = 0; int a = 1; int b = 1; diff --git a/packages/cashc/test/valid-contract-files/for_while_nested.cash b/packages/cashc/test/valid-contract-files/for_while_nested.cash index 517e1839..f2fcd149 100644 --- a/packages/cashc/test/valid-contract-files/for_while_nested.cash +++ b/packages/cashc/test/valid-contract-files/for_while_nested.cash @@ -1,5 +1,5 @@ contract ForWhileNested() { - function spend() { + function spend() public { int sum = 0; for (int i = 0; i < 2; i = i + 1) { diff --git a/packages/cashc/test/valid-contract-files/force_cast_smaller_bytes.cash b/packages/cashc/test/valid-contract-files/force_cast_smaller_bytes.cash index ef83dd89..56ab7ba7 100644 --- a/packages/cashc/test/valid-contract-files/force_cast_smaller_bytes.cash +++ b/packages/cashc/test/valid-contract-files/force_cast_smaller_bytes.cash @@ -1,5 +1,5 @@ contract Test() { - function hello() { + function hello() public { // Have to know what you're doing to force cast bytes3 byte_ = unsafe_bytes3(bytes(0x1234)); require(byte_.length == 1); diff --git a/packages/cashc/test/compiler/UndefinedReferenceError/function_call_with_function_name.cash b/packages/cashc/test/valid-contract-files/function_call_with_function_name.cash similarity index 59% rename from packages/cashc/test/compiler/UndefinedReferenceError/function_call_with_function_name.cash rename to packages/cashc/test/valid-contract-files/function_call_with_function_name.cash index 5be02f7d..0d99ecf4 100644 --- a/packages/cashc/test/compiler/UndefinedReferenceError/function_call_with_function_name.cash +++ b/packages/cashc/test/valid-contract-files/function_call_with_function_name.cash @@ -1,9 +1,9 @@ contract Test(int x) { - function hello() { + function hello() public { require(world(x)); } - function world(int a) { + function world(int a) public { require(a + 5 == 10); } } diff --git a/packages/cashc/test/valid-contract-files/hodl_vault.cash b/packages/cashc/test/valid-contract-files/hodl_vault.cash index dbf2bc8a..6b053882 100644 --- a/packages/cashc/test/valid-contract-files/hodl_vault.cash +++ b/packages/cashc/test/valid-contract-files/hodl_vault.cash @@ -13,7 +13,7 @@ contract HodlVault( sig ownerSig, datasig oracleSig, bytes8 oracleMessage - ) { + ) public { // message: { blockHeight, price } bytes4 blockHeightBin, bytes4 priceBin = oracleMessage.split(4); int blockHeight = int(blockHeightBin); diff --git a/packages/cashc/test/valid-contract-files/if_statement.cash b/packages/cashc/test/valid-contract-files/if_statement.cash index 620ce253..67dd206a 100644 --- a/packages/cashc/test/valid-contract-files/if_statement.cash +++ b/packages/cashc/test/valid-contract-files/if_statement.cash @@ -1,5 +1,5 @@ contract IfStatement(int x, int y) { - function hello(int a, int b) { + function hello(int a, int b) public { int d = a + b; d = d - a; if (d == x - 2) { diff --git a/packages/cashc/test/valid-contract-files/if_statement_number_units-logs.cash b/packages/cashc/test/valid-contract-files/if_statement_number_units-logs.cash index e3994579..09b85c01 100644 --- a/packages/cashc/test/valid-contract-files/if_statement_number_units-logs.cash +++ b/packages/cashc/test/valid-contract-files/if_statement_number_units-logs.cash @@ -1,5 +1,5 @@ contract Test() { - function hello(int a, int b) { + function hello(int a, int b) public { if (a == b - 2 minutes) { require(false); } else if (b == 2 weeks) diff --git a/packages/cashc/test/valid-contract-files/if_statement_number_units.cash b/packages/cashc/test/valid-contract-files/if_statement_number_units.cash index 0cb07f6e..9489a9fb 100644 --- a/packages/cashc/test/valid-contract-files/if_statement_number_units.cash +++ b/packages/cashc/test/valid-contract-files/if_statement_number_units.cash @@ -1,5 +1,5 @@ contract Test() { - function hello(int a, int b) { + function hello(int a, int b) public { if (a == b - 2 minutes) { require(false); } else if (b == 2 weeks) diff --git a/packages/cashc/test/valid-contract-files/int_to_byte.cash b/packages/cashc/test/valid-contract-files/int_to_byte.cash index 18698eff..e491993b 100644 --- a/packages/cashc/test/valid-contract-files/int_to_byte.cash +++ b/packages/cashc/test/valid-contract-files/int_to_byte.cash @@ -1,5 +1,5 @@ contract IntToByte() { - function hello(int a, byte b) { + function hello(int a, byte b) public { byte c = toPaddedBytes(a, 1); require(b == c); } diff --git a/packages/cashc/test/valid-contract-files/integer_formatting.cash b/packages/cashc/test/valid-contract-files/integer_formatting.cash index c1d98d91..3bcf47f6 100644 --- a/packages/cashc/test/valid-contract-files/integer_formatting.cash +++ b/packages/cashc/test/valid-contract-files/integer_formatting.cash @@ -1,5 +1,5 @@ contract IntegerFormatting() { - function test() { + function test() public { int scientific1 = 1e12; int scientific2 = 1E12; int underscores = 1_000_000_000_000; diff --git a/packages/cashc/test/valid-contract-files/internal_helper_functions.cash b/packages/cashc/test/valid-contract-files/internal_helper_functions.cash new file mode 100644 index 00000000..7e87acb5 --- /dev/null +++ b/packages/cashc/test/valid-contract-files/internal_helper_functions.cash @@ -0,0 +1,9 @@ +contract InternalHelperFunctions() { + function spend(int x) public { + require(isTen(x)); + } + + function isTen(int value) internal { + require(value == 10); + } +} diff --git a/packages/cashc/test/valid-contract-files/invert.cash b/packages/cashc/test/valid-contract-files/invert.cash index 463c4ae0..fd744323 100644 --- a/packages/cashc/test/valid-contract-files/invert.cash +++ b/packages/cashc/test/valid-contract-files/invert.cash @@ -1,5 +1,5 @@ contract Test() { - function spend() { + function spend() public { bytes4 x = 0x00000000; bytes4 y = ~x; diff --git a/packages/cashc/test/valid-contract-files/log_intermediate_results.cash b/packages/cashc/test/valid-contract-files/log_intermediate_results.cash index d14e096a..14672a48 100644 --- a/packages/cashc/test/valid-contract-files/log_intermediate_results.cash +++ b/packages/cashc/test/valid-contract-files/log_intermediate_results.cash @@ -1,5 +1,5 @@ contract LogIntermediateResults(pubkey owner) { - function test_log_intermediate_result() { + function test_log_intermediate_result() public { bytes32 singleHash = sha256(owner); console.log(singleHash); bytes32 doubleHash = sha256(singleHash); diff --git a/packages/cashc/test/valid-contract-files/mecenas.cash b/packages/cashc/test/valid-contract-files/mecenas.cash index 563a3aa1..01c069d4 100644 --- a/packages/cashc/test/valid-contract-files/mecenas.cash +++ b/packages/cashc/test/valid-contract-files/mecenas.cash @@ -1,5 +1,5 @@ contract Mecenas(bytes20 recipient, bytes20 funder, int pledge, int period) { - function receive() { + function receive() public { require(this.age >= period); // Check that the first output sends to the recipient @@ -20,7 +20,7 @@ contract Mecenas(bytes20 recipient, bytes20 funder, int pledge, int period) { } } - function reclaim(pubkey pk, sig s) { + function reclaim(pubkey pk, sig s) public { require(hash160(pk) == funder); require(checkSig(s, pk)); } diff --git a/packages/cashc/test/valid-contract-files/multifunction.cash b/packages/cashc/test/valid-contract-files/multifunction.cash index 881aae9e..8311822c 100644 --- a/packages/cashc/test/valid-contract-files/multifunction.cash +++ b/packages/cashc/test/valid-contract-files/multifunction.cash @@ -3,11 +3,11 @@ contract MultiFunction( pubkey recipient, int timeout ) { - function transfer(sig recipientSig) { + function transfer(sig recipientSig) public { require(checkSig(recipientSig, recipient)); } - function timeout(sig senderSig) { + function timeout(sig senderSig) public { require(checkSig(senderSig, sender)); require(tx.time >= timeout); } diff --git a/packages/cashc/test/valid-contract-files/multifunction_if_statements.cash b/packages/cashc/test/valid-contract-files/multifunction_if_statements.cash index b3a1547a..b52455c9 100644 --- a/packages/cashc/test/valid-contract-files/multifunction_if_statements.cash +++ b/packages/cashc/test/valid-contract-files/multifunction_if_statements.cash @@ -1,5 +1,5 @@ contract MultiFunctionIfStatements(int x, int y) { - function transfer(int a, int b) { + function transfer(int a, int b) public { int d = a + b; d = d - a; if (d == x && bool(x)) { @@ -13,7 +13,7 @@ contract MultiFunctionIfStatements(int x, int y) { require(d == y); } - function timeout(int b) { + function timeout(int b) public { int d = b; d = d + 2; if (d == x) { diff --git a/packages/cashc/test/valid-contract-files/multiline_array_multisig.cash b/packages/cashc/test/valid-contract-files/multiline_array_multisig.cash index 1945879e..65ae7dc9 100644 --- a/packages/cashc/test/valid-contract-files/multiline_array_multisig.cash +++ b/packages/cashc/test/valid-contract-files/multiline_array_multisig.cash @@ -1,5 +1,5 @@ contract Test() { - function cms(sig s, pubkey pk) { + function cms(sig s, pubkey pk) public { require(checkMultiSig( [ s, sig(0x00) diff --git a/packages/cashc/test/valid-contract-files/multiline_statements.cash b/packages/cashc/test/valid-contract-files/multiline_statements.cash index 814ae571..5d69f5f8 100644 --- a/packages/cashc/test/valid-contract-files/multiline_statements.cash +++ b/packages/cashc/test/valid-contract-files/multiline_statements.cash @@ -5,7 +5,7 @@ contract MultilineStatements( function spend( int a, string b - ) { + ) public { if (a == x - 2 && b == y) { require(false); diff --git a/packages/cashc/test/valid-contract-files/multiplication.cash b/packages/cashc/test/valid-contract-files/multiplication.cash index 14184412..fd1d50ac 100644 --- a/packages/cashc/test/valid-contract-files/multiplication.cash +++ b/packages/cashc/test/valid-contract-files/multiplication.cash @@ -1,5 +1,5 @@ contract Test(int x) { - function hello() { + function hello() public { int myVariable = 10 - 4; int myOtherVariable = 20 * myVariable % 2; require(myOtherVariable > x); diff --git a/packages/cashc/test/valid-contract-files/num2bin.cash b/packages/cashc/test/valid-contract-files/num2bin.cash index 6d06a21d..74fa046e 100644 --- a/packages/cashc/test/valid-contract-files/num2bin.cash +++ b/packages/cashc/test/valid-contract-files/num2bin.cash @@ -1,5 +1,5 @@ contract Test() { - function hello() { + function hello() public { bytes2 byte_ = toPaddedBytes(10, 2); require(int(byte_) == 10); } diff --git a/packages/cashc/test/valid-contract-files/num2bin_variable.cash b/packages/cashc/test/valid-contract-files/num2bin_variable.cash index a86db2ba..b1b32f67 100644 --- a/packages/cashc/test/valid-contract-files/num2bin_variable.cash +++ b/packages/cashc/test/valid-contract-files/num2bin_variable.cash @@ -1,5 +1,5 @@ contract Num2Bin() { - function spend(int size) { + function spend(int size) public { bytes byte_ = toPaddedBytes(10, size); require(int(byte_) == 10); } diff --git a/packages/cashc/test/valid-contract-files/p2palindrome.cash b/packages/cashc/test/valid-contract-files/p2palindrome.cash index f39a07be..f3930b97 100644 --- a/packages/cashc/test/valid-contract-files/p2palindrome.cash +++ b/packages/cashc/test/valid-contract-files/p2palindrome.cash @@ -1,5 +1,5 @@ contract P2Palindrome() { - function spend(string palindrome) { + function spend(string palindrome) public { require(palindrome.reverse() == palindrome); } } diff --git a/packages/cashc/test/valid-contract-files/p2pkh-logs.cash b/packages/cashc/test/valid-contract-files/p2pkh-logs.cash index fa6f2765..06152d4d 100644 --- a/packages/cashc/test/valid-contract-files/p2pkh-logs.cash +++ b/packages/cashc/test/valid-contract-files/p2pkh-logs.cash @@ -1,5 +1,5 @@ contract P2PKH(bytes20 pkh) { - function spend(pubkey pk, sig s) { + function spend(pubkey pk, sig s) public { require(hash160(pk) == pkh); console.log(pk, pkh, s); require(checkSig(s, pk)); diff --git a/packages/cashc/test/valid-contract-files/p2pkh.cash b/packages/cashc/test/valid-contract-files/p2pkh.cash index 3053dacc..4f60c59c 100644 --- a/packages/cashc/test/valid-contract-files/p2pkh.cash +++ b/packages/cashc/test/valid-contract-files/p2pkh.cash @@ -1,5 +1,5 @@ contract P2PKH(bytes20 pkh) { - function spend(pubkey pk, sig s) { + function spend(pubkey pk, sig s) public { require(hash160(pk) == pkh); require(checkSig(s, pk)); } diff --git a/packages/cashc/test/valid-contract-files/p2pkh_with_assignment.cash b/packages/cashc/test/valid-contract-files/p2pkh_with_assignment.cash index eb849907..0db191cb 100644 --- a/packages/cashc/test/valid-contract-files/p2pkh_with_assignment.cash +++ b/packages/cashc/test/valid-contract-files/p2pkh_with_assignment.cash @@ -1,7 +1,7 @@ pragma cashscript >=0.8.0; contract P2PKH(bytes20 pkh) { - function spend(pubkey pk, sig s) { + function spend(pubkey pk, sig s) public { bytes20 passedPkh = hash160(pk); require(passedPkh == pkh); require(checkSig(s, pk)); diff --git a/packages/cashc/test/valid-contract-files/p2pkh_with_cast.cash b/packages/cashc/test/valid-contract-files/p2pkh_with_cast.cash index 91152dac..b2412529 100644 --- a/packages/cashc/test/valid-contract-files/p2pkh_with_cast.cash +++ b/packages/cashc/test/valid-contract-files/p2pkh_with_cast.cash @@ -1,7 +1,7 @@ pragma cashscript >=0.8.0; contract P2PKH(bytes20 pkh) { - function spend(pubkey pk, bytes65 s) { + function spend(pubkey pk, bytes65 s) public { require(hash160(pk) == pkh); require(checkSig(sig(s), pk)); } diff --git a/packages/cashc/test/valid-contract-files/reassignment.cash b/packages/cashc/test/valid-contract-files/reassignment.cash index 332e3458..4d58cbd2 100644 --- a/packages/cashc/test/valid-contract-files/reassignment.cash +++ b/packages/cashc/test/valid-contract-files/reassignment.cash @@ -1,5 +1,5 @@ contract Reassignment(int x, string y) { - function hello(pubkey pk, sig s) { + function hello(pubkey pk, sig s) public { int myVariable = 10 - 4; int myOtherVariable = 20 + myVariable % 2; require(myOtherVariable > x); diff --git a/packages/cashc/test/valid-contract-files/simple_cast.cash b/packages/cashc/test/valid-contract-files/simple_cast.cash index c0f337b3..3752cd6e 100644 --- a/packages/cashc/test/valid-contract-files/simple_cast.cash +++ b/packages/cashc/test/valid-contract-files/simple_cast.cash @@ -1,5 +1,5 @@ contract Test(int x, string y) { - function hello(sig s, pubkey pk) { + function hello(sig s, pubkey pk) public { int myVariable = 10 - int(true); int myOtherVariable = 20 + myVariable % 2; require(myOtherVariable > x); diff --git a/packages/cashc/test/valid-contract-files/simple_checkdatasig.cash b/packages/cashc/test/valid-contract-files/simple_checkdatasig.cash index 1e445a4d..d82c9b3d 100644 --- a/packages/cashc/test/valid-contract-files/simple_checkdatasig.cash +++ b/packages/cashc/test/valid-contract-files/simple_checkdatasig.cash @@ -1,5 +1,5 @@ contract Test(datasig s, pubkey pk) { - function cds(bytes data) { + function cds(bytes data) public { require(checkDataSig(s, data, pk)); } } diff --git a/packages/cashc/test/valid-contract-files/simple_constant.cash b/packages/cashc/test/valid-contract-files/simple_constant.cash index c8d16b99..48e2bd80 100644 --- a/packages/cashc/test/valid-contract-files/simple_constant.cash +++ b/packages/cashc/test/valid-contract-files/simple_constant.cash @@ -1,5 +1,5 @@ contract Test() { - function hello() { + function hello() public { string constant m = "hello"; require(m == "hello"); } diff --git a/packages/cashc/test/valid-contract-files/simple_covenant.cash b/packages/cashc/test/valid-contract-files/simple_covenant.cash index 4546db79..78dd1da3 100644 --- a/packages/cashc/test/valid-contract-files/simple_covenant.cash +++ b/packages/cashc/test/valid-contract-files/simple_covenant.cash @@ -1,5 +1,5 @@ contract Test() { - function covenant() { + function covenant() public { require(tx.version == 2); } } diff --git a/packages/cashc/test/valid-contract-files/simple_functions.cash b/packages/cashc/test/valid-contract-files/simple_functions.cash index 21d62ba1..ba141649 100644 --- a/packages/cashc/test/valid-contract-files/simple_functions.cash +++ b/packages/cashc/test/valid-contract-files/simple_functions.cash @@ -1,9 +1,9 @@ contract Test() { - function hello(sig s, pubkey pk) { + function hello(sig s, pubkey pk) public { require(checkSig(s, pk)); } - function world(int a) { + function world(int a) public { require(a + 5 == 10); } } diff --git a/packages/cashc/test/valid-contract-files/simple_if_statement.cash b/packages/cashc/test/valid-contract-files/simple_if_statement.cash index 03221367..2307fe08 100644 --- a/packages/cashc/test/valid-contract-files/simple_if_statement.cash +++ b/packages/cashc/test/valid-contract-files/simple_if_statement.cash @@ -1,5 +1,5 @@ contract Test(int x, string y) { - function hello(int a, string b) { + function hello(int a, string b) public { if (a == x - 2) { require(false); } else if (b == "Hello " + y) diff --git a/packages/cashc/test/valid-contract-files/simple_multisig.cash b/packages/cashc/test/valid-contract-files/simple_multisig.cash index 5babb26d..88d68405 100644 --- a/packages/cashc/test/valid-contract-files/simple_multisig.cash +++ b/packages/cashc/test/valid-contract-files/simple_multisig.cash @@ -1,5 +1,5 @@ contract Test() { - function cms(sig s, pubkey pk) { + function cms(sig s, pubkey pk) public { require(checkMultiSig([s, sig(0x00)], [pk, pubkey(0x00)])); } } diff --git a/packages/cashc/test/valid-contract-files/simple_splice.cash b/packages/cashc/test/valid-contract-files/simple_splice.cash index 2a9edbbc..dc40cf22 100644 --- a/packages/cashc/test/valid-contract-files/simple_splice.cash +++ b/packages/cashc/test/valid-contract-files/simple_splice.cash @@ -1,5 +1,5 @@ contract Test(bytes b) { - function spend() { + function spend() public { bytes x = b.split(5)[1]; require(x != b); require (b.split(4)[0] != x); diff --git a/packages/cashc/test/valid-contract-files/simple_variables.cash b/packages/cashc/test/valid-contract-files/simple_variables.cash index 31a1069f..0584e08c 100644 --- a/packages/cashc/test/valid-contract-files/simple_variables.cash +++ b/packages/cashc/test/valid-contract-files/simple_variables.cash @@ -1,5 +1,5 @@ contract Test(int x, string y) { - function hello(sig s, pubkey pk) { + function hello(sig s, pubkey pk) public { int myVariable = 10 - 4; int myOtherVariable = 20 + myVariable % 2; require(myOtherVariable > x); diff --git a/packages/cashc/test/valid-contract-files/simulating_state.cash b/packages/cashc/test/valid-contract-files/simulating_state.cash index f2b0b3a9..d2154f29 100644 --- a/packages/cashc/test/valid-contract-files/simulating_state.cash +++ b/packages/cashc/test/valid-contract-files/simulating_state.cash @@ -4,7 +4,7 @@ contract SimulatingState( int pledgePerBlock, bytes8 initialBlock, ) { - function receive() { + function receive() public { // Check that the first output sends to the recipient bytes25 recipientLockingBytecode = new LockingBytecodeP2PKH(recipient); require(tx.outputs[0].lockingBytecode == recipientLockingBytecode); @@ -45,7 +45,7 @@ contract SimulatingState( } } - function reclaim(pubkey pk, sig s) { + function reclaim(pubkey pk, sig s) public { require(hash160(pk) == funder); require(checkSig(s, pk)); } diff --git a/packages/cashc/test/valid-contract-files/slice.cash b/packages/cashc/test/valid-contract-files/slice.cash index 087014f0..e9806190 100644 --- a/packages/cashc/test/valid-contract-files/slice.cash +++ b/packages/cashc/test/valid-contract-files/slice.cash @@ -1,5 +1,5 @@ contract Slice(bytes20 pkh) { - function spend() { + function spend() public { bytes actualPkh = tx.inputs[this.activeInputIndex].lockingBytecode.slice(3, 23); require(pkh == actualPkh); } diff --git a/packages/cashc/test/valid-contract-files/slice_optimised.cash b/packages/cashc/test/valid-contract-files/slice_optimised.cash index d6f92390..7b3c6d68 100644 --- a/packages/cashc/test/valid-contract-files/slice_optimised.cash +++ b/packages/cashc/test/valid-contract-files/slice_optimised.cash @@ -1,5 +1,5 @@ contract Slice(bytes32 data) { - function spend() { + function spend() public { bytes20 pkh = data.slice(0, 20); require(pkh == toPaddedBytes(0, 20)); } diff --git a/packages/cashc/test/valid-contract-files/slice_variable_parameter.cash b/packages/cashc/test/valid-contract-files/slice_variable_parameter.cash index 46e914c9..845fbe73 100644 --- a/packages/cashc/test/valid-contract-files/slice_variable_parameter.cash +++ b/packages/cashc/test/valid-contract-files/slice_variable_parameter.cash @@ -1,5 +1,5 @@ contract Slice(bytes20 pkh) { - function spend() { + function spend() public { int x = 3; bytes actualPkh = tx.inputs[this.activeInputIndex].lockingBytecode.slice(x, 23); require(pkh == actualPkh); diff --git a/packages/cashc/test/valid-contract-files/split_or_slice_signature.cash b/packages/cashc/test/valid-contract-files/split_or_slice_signature.cash index 98652eaf..82ae9a54 100644 --- a/packages/cashc/test/valid-contract-files/split_or_slice_signature.cash +++ b/packages/cashc/test/valid-contract-files/split_or_slice_signature.cash @@ -1,5 +1,5 @@ contract Test(sig signature) { - function spend() { + function spend() public { // Assume Schnorr bytes hashtype1 = signature.split(64)[1]; bytes1 hashtype2 = signature.slice(64, 65); diff --git a/packages/cashc/test/valid-contract-files/split_size.cash b/packages/cashc/test/valid-contract-files/split_size.cash index 2499888c..d0d90b6e 100644 --- a/packages/cashc/test/valid-contract-files/split_size.cash +++ b/packages/cashc/test/valid-contract-files/split_size.cash @@ -1,5 +1,5 @@ contract SplitSize(bytes b) { - function spend() { + function spend() public { bytes x = b.split(b.length / 2)[1]; require(x != b); require(b.split(4)[0] != x); diff --git a/packages/cashc/test/valid-contract-files/split_typed.cash b/packages/cashc/test/valid-contract-files/split_typed.cash index b7a8fcf8..536347d0 100644 --- a/packages/cashc/test/valid-contract-files/split_typed.cash +++ b/packages/cashc/test/valid-contract-files/split_typed.cash @@ -1,5 +1,5 @@ contract SplitTyped(bytes b) { - function spend() { + function spend() public { bytes4 x = b.split(4)[0]; require(x != b); } diff --git a/packages/cashc/test/valid-contract-files/string_concatenation.cash b/packages/cashc/test/valid-contract-files/string_concatenation.cash index f615a66c..19374762 100644 --- a/packages/cashc/test/valid-contract-files/string_concatenation.cash +++ b/packages/cashc/test/valid-contract-files/string_concatenation.cash @@ -1,5 +1,5 @@ contract Test() { - function hello(string who) { + function hello(string who) public { require(("hello " + who).length + 2 > 5); } } diff --git a/packages/cashc/test/valid-contract-files/string_with_escaped_characters.cash b/packages/cashc/test/valid-contract-files/string_with_escaped_characters.cash index 5b44e10d..c51ddbbf 100644 --- a/packages/cashc/test/valid-contract-files/string_with_escaped_characters.cash +++ b/packages/cashc/test/valid-contract-files/string_with_escaped_characters.cash @@ -1,5 +1,5 @@ contract Test(int x) { - function hello() { + function hello() public { int myVariable = 10 - 4; int myOtherVariable = 20 + myVariable % 2; require(myOtherVariable > x); diff --git a/packages/cashc/test/valid-contract-files/sum_input_amount.cash b/packages/cashc/test/valid-contract-files/sum_input_amount.cash index ebfefd44..e216d740 100644 --- a/packages/cashc/test/valid-contract-files/sum_input_amount.cash +++ b/packages/cashc/test/valid-contract-files/sum_input_amount.cash @@ -1,5 +1,5 @@ contract Loopy() { - function doLoop() { + function doLoop() public { int sum = 0; int i = 0; diff --git a/packages/cashc/test/valid-contract-files/token_category_comparison.cash b/packages/cashc/test/valid-contract-files/token_category_comparison.cash index a4dacfa7..e40b15c7 100644 --- a/packages/cashc/test/valid-contract-files/token_category_comparison.cash +++ b/packages/cashc/test/valid-contract-files/token_category_comparison.cash @@ -1,5 +1,5 @@ contract Test() { - function send() { + function send() public { require(tx.inputs[1].tokenCategory == 0x); } } diff --git a/packages/cashc/test/valid-contract-files/trailing_comma.cash b/packages/cashc/test/valid-contract-files/trailing_comma.cash index 29848b6b..6a040248 100644 --- a/packages/cashc/test/valid-contract-files/trailing_comma.cash +++ b/packages/cashc/test/valid-contract-files/trailing_comma.cash @@ -6,7 +6,7 @@ contract Contract( sig ownerSig, datasig oracleMsgSig, sig oracleTxSig, - ) { + ) public { bytes oracleMessage = bytes('Spend') + bytes(12,); require(checkDataSig( oracleMsgSig, diff --git a/packages/cashc/test/valid-contract-files/tuple_unpacking.cash b/packages/cashc/test/valid-contract-files/tuple_unpacking.cash index ddee9238..1b4a68a9 100644 --- a/packages/cashc/test/valid-contract-files/tuple_unpacking.cash +++ b/packages/cashc/test/valid-contract-files/tuple_unpacking.cash @@ -1,5 +1,5 @@ contract Test() { - function split() { + function split() public { string s1 = "hello"; string s2 = "there"; string hello, string there = (s1+s2).split(5); diff --git a/packages/cashc/test/valid-contract-files/tuple_unpacking_parameter.cash b/packages/cashc/test/valid-contract-files/tuple_unpacking_parameter.cash index 31b7f3f2..65c04381 100644 --- a/packages/cashc/test/valid-contract-files/tuple_unpacking_parameter.cash +++ b/packages/cashc/test/valid-contract-files/tuple_unpacking_parameter.cash @@ -1,5 +1,5 @@ contract Test() { - function split(bytes32 b) { + function split(bytes32 b) public { bytes16 x, bytes16 y = b.split(16); require(x == y); } diff --git a/packages/cashc/test/valid-contract-files/tuple_unpacking_single_side_type.cash b/packages/cashc/test/valid-contract-files/tuple_unpacking_single_side_type.cash index b4dfa7c5..d4c2212d 100644 --- a/packages/cashc/test/valid-contract-files/tuple_unpacking_single_side_type.cash +++ b/packages/cashc/test/valid-contract-files/tuple_unpacking_single_side_type.cash @@ -1,5 +1,5 @@ contract Test() { - function split(bytes b) { + function split(bytes b) public { bytes16 x, bytes y = b.split(16); require(x == y); } diff --git a/packages/cashc/test/valid-contract-files/type_enforcement.cash b/packages/cashc/test/valid-contract-files/type_enforcement.cash index 327b835d..8d112a7c 100644 --- a/packages/cashc/test/valid-contract-files/type_enforcement.cash +++ b/packages/cashc/test/valid-contract-files/type_enforcement.cash @@ -4,7 +4,7 @@ contract TypeEnforcement() { bool enforcedBool, bytes4 enforcedBytes, bytes nonEnforcedBytes - ) { + ) public { if (enforcedBool == true) { require(nonEnforcedInt > 6); } diff --git a/packages/cashc/test/valid-contract-files/unsafe_bool_cast.cash b/packages/cashc/test/valid-contract-files/unsafe_bool_cast.cash index da63b155..8fbc5982 100644 --- a/packages/cashc/test/valid-contract-files/unsafe_bool_cast.cash +++ b/packages/cashc/test/valid-contract-files/unsafe_bool_cast.cash @@ -1,5 +1,5 @@ contract Test(int x) { - function test() { + function test() public { require(unsafe_bool(x)); } } diff --git a/packages/cashc/test/valid-contract-files/unsafe_int_cast.cash b/packages/cashc/test/valid-contract-files/unsafe_int_cast.cash index 97212d94..f78a7db1 100644 --- a/packages/cashc/test/valid-contract-files/unsafe_int_cast.cash +++ b/packages/cashc/test/valid-contract-files/unsafe_int_cast.cash @@ -1,5 +1,5 @@ contract Test(bytes4 x) { - function test(bytes4 y) { + function test(bytes4 y) public { require(unsafe_int(x) > unsafe_int(y)); } } diff --git a/packages/cashc/test/valid-contract-files/while_loop.cash b/packages/cashc/test/valid-contract-files/while_loop.cash index e88d0222..8c2bca46 100644 --- a/packages/cashc/test/valid-contract-files/while_loop.cash +++ b/packages/cashc/test/valid-contract-files/while_loop.cash @@ -1,5 +1,5 @@ contract Loopy() { - function doLoop() { + function doLoop() public { int i = 0; while (i < tx.inputs.length) { diff --git a/packages/cashc/test/valid-contract-files/while_loop_basic.cash b/packages/cashc/test/valid-contract-files/while_loop_basic.cash index 789850c4..b47b9430 100644 --- a/packages/cashc/test/valid-contract-files/while_loop_basic.cash +++ b/packages/cashc/test/valid-contract-files/while_loop_basic.cash @@ -1,5 +1,5 @@ contract WhileLoopBasic() { - function spend() { + function spend() public { int i = 0; while (i < 3) { diff --git a/packages/cashc/test/valid-contract-files/while_loop_nested.cash b/packages/cashc/test/valid-contract-files/while_loop_nested.cash index ed79c7d2..a8aec215 100644 --- a/packages/cashc/test/valid-contract-files/while_loop_nested.cash +++ b/packages/cashc/test/valid-contract-files/while_loop_nested.cash @@ -1,5 +1,5 @@ contract WhileLoopNested() { - function spend() { + function spend() public { int i = 0; int total = 0; diff --git a/packages/cashscript/src/Contract.ts b/packages/cashscript/src/Contract.ts index 0a02600e..9cf93acb 100644 --- a/packages/cashscript/src/Contract.ts +++ b/packages/cashscript/src/Contract.ts @@ -72,6 +72,15 @@ class ContractInternal< throw new Error(`Artifact compiled with unsupported compiler version: ${artifact.compiler.version}, required >=0.7.0`); } + if ( + artifact.compiler.target + && this.provider.vmTarget + && this.provider.vmTarget !== artifact.compiler.target + && this.provider.vmTarget !== 'BCH_SPEC' + ) { + throw new Error(`Artifact requires VM target ${artifact.compiler.target}, but the current provider is configured for ${this.provider.vmTarget}`); + } + if (artifact.constructorInputs.length !== constructorArgs.length) { throw new Error(`Incorrect number of arguments passed to ${artifact.contractName} constructor. Expected ${artifact.constructorInputs.length} arguments (${artifact.constructorInputs.map((input) => input.type)}) but got ${constructorArgs.length}`); } diff --git a/packages/cashscript/src/Errors.ts b/packages/cashscript/src/Errors.ts index 7a8a69c4..323c9e3c 100644 --- a/packages/cashscript/src/Errors.ts +++ b/packages/cashscript/src/Errors.ts @@ -1,4 +1,4 @@ -import { Artifact, RequireStatement, sourceMapToLocationData, Type } from '@cashscript/utils'; +import { Artifact, DebugFrame, RequireStatement, sourceMapToLocationData, Type } from '@cashscript/utils'; export class TypeError extends Error { constructor(actual: string, expected: Type) { @@ -68,12 +68,19 @@ export class FailedTransactionEvaluationError extends FailedTransactionError { public inputIndex: number, public bitauthUri: string, public libauthErrorMessage: string, + public frameId?: string, + public frameBytecode?: string, ) { let message = `${artifact.contractName}.cash Error in transaction at input ${inputIndex} in contract ${artifact.contractName}.cash.\nReason: ${libauthErrorMessage}`; if (artifact.debug) { - const { statement, lineNumber } = getLocationDataForInstructionPointer(artifact, failingInstructionPointer); - message = `${artifact.contractName}.cash:${lineNumber} Error in transaction at input ${inputIndex} in contract ${artifact.contractName}.cash at line ${lineNumber}.\nReason: ${libauthErrorMessage}\nFailing statement: ${statement}`; + const { statement, lineNumber, sourceName } = getLocationDataForInstructionPointer( + artifact, + failingInstructionPointer, + frameId, + frameBytecode, + ); + message = `${sourceName}:${lineNumber} Error in transaction at input ${inputIndex} in contract ${artifact.contractName}.cash at line ${lineNumber}.\nReason: ${libauthErrorMessage}\nFailing statement: ${statement}`; } super(message, bitauthUri); @@ -89,9 +96,22 @@ export class FailedRequireError extends FailedTransactionError { public bitauthUri: string, public libauthErrorMessage?: string, ) { - const { statement, lineNumber } = getLocationDataForInstructionPointer(artifact, failingInstructionPointer); - - const baseMessage = `${artifact.contractName}.cash:${lineNumber} Require statement failed at input ${inputIndex} in contract ${artifact.contractName}.cash at line ${lineNumber}`; + const { statement, lineNumber, sourceName } = requireStatement.location + ? getLocationDataForLocation( + artifact, + requireStatement.location, + requireStatement.frameId, + requireStatement.frameBytecode, + requireStatement.sourceFile, + ) + : getLocationDataForInstructionPointer( + artifact, + failingInstructionPointer, + requireStatement.frameId, + requireStatement.frameBytecode, + ); + + const baseMessage = `${sourceName}:${lineNumber} Require statement failed at input ${inputIndex} in contract ${artifact.contractName}.cash at line ${lineNumber}`; const baseMessageWithRequireMessage = `${baseMessage} with the following message: ${requireStatement.message}`; const fullMessage = `${requireStatement.message ? baseMessageWithRequireMessage : baseMessage}.\nFailing statement: ${statement}`; @@ -102,24 +122,90 @@ export class FailedRequireError extends FailedTransactionError { const getLocationDataForInstructionPointer = ( artifact: Artifact, instructionPointer: number, -): { lineNumber: number, statement: string } => { - const locationData = sourceMapToLocationData(artifact.debug!.sourceMap); - - // We subtract the constructor inputs because these are present in the evaluation (and thus the instruction pointer) - // but they are not present in the source code (and thus the location data) - const modifiedInstructionPointer = instructionPointer - artifact.constructorInputs.length; + frameId?: string, + frameBytecode?: string, +): { lineNumber: number; statement: string; sourceName: string } => { + const frame = getDebugFrame(artifact, frameId, frameBytecode); + const locationData = sourceMapToLocationData(frame.sourceMap); + const modifiedInstructionPointer = instructionPointer - ( + frame.id === '__root__' ? artifact.constructorInputs.length : 0 + ); const { location } = locationData[modifiedInstructionPointer]; + const source = frame.source; + const failingLines = source.split('\n').slice(location.start.line - 1, location.end.line); - const failingLines = artifact.source.split('\n').slice(location.start.line - 1, location.end.line); - - // Slice off the start and end of the statement's start and end lines to only return the failing part - // Note that we first slice off the end, to avoid shifting the end column index failingLines[failingLines.length - 1] = failingLines[failingLines.length - 1].slice(0, location.end.column); failingLines[0] = failingLines[0].slice(location.start.column); - const statement = failingLines.join('\n'); - const lineNumber = location.start.line; + return { + statement: failingLines.join('\n'), + lineNumber: location.start.line, + sourceName: getSourceName(frame.sourceFile, artifact), + }; +}; + +const getLocationDataForLocation = ( + artifact: Artifact, + location: NonNullable, + frameId?: string, + frameBytecode?: string, + sourceFile?: string, +): { lineNumber: number; statement: string; sourceName: string } => { + const frame = getDebugFrame(artifact, frameId, frameBytecode, sourceFile); + const failingLines = frame.source.split('\n').slice(location.start.line - 1, location.end.line); - return { statement, lineNumber }; + failingLines[failingLines.length - 1] = failingLines[failingLines.length - 1].slice(0, location.end.column); + failingLines[0] = failingLines[0].slice(location.start.column); + + return { + lineNumber: location.start.line, + statement: failingLines.join('\n'), + sourceName: getSourceName(sourceFile ?? frame.sourceFile, artifact), + }; }; + +function getDebugFrame( + artifact: Artifact, + frameId?: string, + frameBytecode?: string, + sourceFile?: string, +): DebugFrame { + const rootFrame: DebugFrame = { + id: '__root__', + bytecode: artifact.debug!.bytecode, + sourceMap: artifact.debug!.sourceMap, + source: artifact.source, + }; + + const frames = artifact.debug?.frames ?? []; + + if (frameId) { + const matchingFrame = frames.find((frame) => frame.id === frameId); + if (matchingFrame) return matchingFrame; + if (frameId === '__root__') return rootFrame; + } + + if (frameBytecode) { + const matchingFrame = frames.find((frame) => ( + frame.bytecode === frameBytecode || frame.bytecode.endsWith(frameBytecode) + )); + if (matchingFrame) return matchingFrame; + if (rootFrame.bytecode === frameBytecode || rootFrame.bytecode.endsWith(frameBytecode)) return rootFrame; + } + + if (sourceFile) { + const matchingFrame = frames.find((frame) => frame.sourceFile === sourceFile); + if (matchingFrame) return matchingFrame; + } + + return rootFrame; +} + +function getSourceName(sourceFile: string | undefined, artifact: Artifact): string { + return sourceFile ? basename(sourceFile) : `${artifact.contractName}.cash`; +} + +function basename(sourceFile: string): string { + return sourceFile.split(/[/\\]/).at(-1) ?? sourceFile; +} diff --git a/packages/cashscript/src/TransactionBuilder.ts b/packages/cashscript/src/TransactionBuilder.ts index 4d6d0e42..221f57c0 100644 --- a/packages/cashscript/src/TransactionBuilder.ts +++ b/packages/cashscript/src/TransactionBuilder.ts @@ -248,9 +248,9 @@ export class TransactionBuilder { .map((input) => 'contract' in input.unlocker ? input.unlocker.contract.artifact.compiler.version : null) .filter((version) => version !== null); - if (!contractVersions.every((version) => semver.satisfies(version, '>=0.11.0'))) { - console.warn('For the best debugging experience, please recompile your contract with cashc version 0.11.0 or newer.'); - } + if (!contractVersions.every((version) => semver.satisfies(version, '>=0.11.0', { includePrerelease: true }))) { + console.warn('For the best debugging experience, please recompile your contract with cashc version 0.11.0 or newer.'); + } return debugLibauthTemplate(this.getLibauthTemplate(), this); } diff --git a/packages/cashscript/src/debugging.ts b/packages/cashscript/src/debugging.ts index d5bf3991..b0f5bf42 100644 --- a/packages/cashscript/src/debugging.ts +++ b/packages/cashscript/src/debugging.ts @@ -1,5 +1,5 @@ import { AuthenticationErrorCommon, AuthenticationInstruction, AuthenticationProgramCommon, AuthenticationProgramStateCommon, AuthenticationVirtualMachine, ResolvedTransactionCommon, WalletTemplate, WalletTemplateScriptUnlocking, binToHex, createCompiler, createVirtualMachineBch2023, createVirtualMachineBch2025, createVirtualMachineBch2026, createVirtualMachineBchSpec, decodeAuthenticationInstructions, encodeAuthenticationInstruction, walletTemplateToCompilerConfiguration } from '@bitauth/libauth'; -import { Artifact, LogData, LogEntry, Op, PrimitiveType, StackItem, asmToBytecode, bytecodeToAsm, decodeBool, decodeInt, decodeString } from '@cashscript/utils'; +import { Artifact, LogData, LogEntry, Op, PrimitiveType, RequireStatement, StackItem, asmToBytecode, bytecodeToAsm, decodeBool, decodeInt, decodeString } from '@cashscript/utils'; import { findLastIndex, toRegExp } from './utils.js'; import { FailedRequireError, FailedTransactionError, FailedTransactionEvaluationError } from './Errors.js'; import { getBitauthUri } from './libauth-template/LibauthTemplate.js'; @@ -67,12 +67,16 @@ const debugSingleScenario = ( template: WalletTemplate, artifact: Artifact | undefined, unlockingScriptId: string, scenarioId: string, ): DebugResult => { const { vm, program } = createProgram(template, unlockingScriptId, scenarioId); + const frameBytecodeCache = new WeakMap(); const fullDebugSteps = vm.debug(program); + const lockingScriptStartIndex = getLockingScriptStartIndex(fullDebugSteps, artifact); - // P2SH executions have 3 phases, we only want the last one (locking script execution) + // P2SH executions have 3 phases, and BCH functions can introduce nested bytecode frames with `ip === 0`. + // We slice from the first step whose active bytecode ends with the contract bytecode, rather than from the + // last `ip === 0`, so invoked helper functions do not get mistaken for the start of the locking script phase. // https://libauth.org/types/AuthenticationVirtualMachine.html#__type.debug - const lockingScriptDebugResult = fullDebugSteps.slice(findLastIndex(fullDebugSteps, (state) => state.ip === 0)); + const lockingScriptDebugResult = fullDebugSteps.slice(lockingScriptStartIndex); // The controlStack determines whether the current debug step is in the executed branch // It also tracks loop / function usage, but for the purpose of determining whether a step was executed, @@ -89,21 +93,31 @@ const debugSingleScenario = ( // Also note that multiple log statements may exist for the same ip, so we need to handle all of them const executedLogs = executedDebugSteps .flatMap((debugStep, index) => { - const logEntries = artifact.debug?.logs?.filter((log) => log.ip === debugStep.ip); + const logEntries = artifact.debug?.logs?.filter((log) => ( + log.ip === debugStep.ip + && matchesDebugFrame(debugStep, log.frameId, log.frameBytecode, frameBytecodeCache) + )); if (!logEntries || logEntries.length === 0) return []; const reversedPriorDebugSteps = executedDebugSteps.slice(0, index + 1).reverse(); return logEntries.map((logEntry) => { const decodedLogData = logEntry.data - .map((dataEntry) => decodeLogDataEntry(dataEntry, reversedPriorDebugSteps, vm)); + .map((dataEntry) => decodeLogDataEntry( + dataEntry, + reversedPriorDebugSteps, + vm, + logEntry.frameId, + logEntry.frameBytecode, + frameBytecodeCache, + )); return { logEntry, decodedLogData }; }); }); for (const { logEntry, decodedLogData } of executedLogs) { const inputIndex = extractInputIndexFromScenario(scenarioId); - logConsoleLogStatement(logEntry, decodedLogData, artifact.contractName, inputIndex); + logConsoleLogStatement(logEntry, decodedLogData, artifact, inputIndex); } } @@ -136,7 +150,10 @@ const debugSingleScenario = ( } const requireStatement = (artifact.debug?.requires ?? []) - .find((statement) => statement.ip === requireStatementIp); + .find((statement) => ( + statement.ip === requireStatementIp + && matchesDebugFrame(lastExecutedDebugStep, statement.frameId, statement.frameBytecode, frameBytecodeCache) + )); if (requireStatement) { // Note that we use failingIp here rather than requireStatementIp, see comment above @@ -147,7 +164,13 @@ const debugSingleScenario = ( // Note that we use failingIp here rather than requireStatementIp, see comment above throw new FailedTransactionEvaluationError( - artifact, failingIp, inputIndex, getBitauthUri(template), error, + artifact, + failingIp, + inputIndex, + getBitauthUri(template), + error, + getFrameId(lastExecutedDebugStep), + getFrameBytecode(lastExecutedDebugStep, frameBytecodeCache), ); } @@ -176,7 +199,10 @@ const debugSingleScenario = ( } const requireStatement = (artifact.debug?.requires ?? []) - .find((message) => message.ip === finalExecutedVerifyIp); + .find((message) => ( + message.ip === finalExecutedVerifyIp + && matchesDebugFrame(lastExecutedDebugStep, message.frameId, message.frameBytecode, frameBytecodeCache) + )); if (requireStatement) { throw new FailedRequireError( @@ -184,14 +210,66 @@ const debugSingleScenario = ( ); } + const nestedRequireStatement = findNestedFinalRequireBeforeFinalVerify( + executedDebugSteps, + artifact.debug?.requires ?? [], + frameBytecodeCache, + ); + + if (nestedRequireStatement) { + throw new FailedRequireError( + artifact, + sourcemapInstructionPointer, + nestedRequireStatement, + inputIndex, + getBitauthUri(template), + ); + } + throw new FailedTransactionEvaluationError( - artifact, sourcemapInstructionPointer, inputIndex, getBitauthUri(template), evaluationResult, + artifact, + sourcemapInstructionPointer, + inputIndex, + getBitauthUri(template), + evaluationResult, + getFrameId(lastExecutedDebugStep), + getFrameBytecode(lastExecutedDebugStep, frameBytecodeCache), ); } return fullDebugSteps; }; +const instructionsToBytecodeHex = (instructions: AuthenticationInstruction[]): string => ( + binToHex(Uint8Array.from(instructions.flatMap((instruction) => Array.from(encodeAuthenticationInstruction(instruction))))) +); + +const getLockingScriptStartIndex = ( + fullDebugSteps: AuthenticationProgramStateCommon[], + artifact: Artifact | undefined, +): number => { + const rootFrameStartIndex = findLastIndex( + fullDebugSteps, + (state) => state.ip === 0 && state.controlStack.length === 0, + ); + + if (rootFrameStartIndex !== -1) { + return rootFrameStartIndex; + } + + if (artifact?.debug?.bytecode) { + const matchingBytecodeIndex = fullDebugSteps.findIndex((state) => ( + state.ip === 0 && instructionsToBytecodeHex(state.instructions).endsWith(artifact.debug!.bytecode) + )); + + if (matchingBytecodeIndex !== -1) { + return matchingBytecodeIndex; + } + } + + return findLastIndex(fullDebugSteps, (state) => state.ip === 0); +}; + // Note: this relies on the naming convention that the scenario ID is of the form _input_evaluate const extractInputIndexFromScenario = (scenarioId: string): number => { const match = scenarioId.match(/_input(\d+)_/); @@ -236,20 +314,34 @@ const createProgram = (template: WalletTemplate, unlockingScriptId: string, scen const logConsoleLogStatement = ( log: LogEntry, decodedLogData: Array, - contractName: string, + artifact: Artifact, inputIndex: number, ): void => { - console.log(`[Input #${inputIndex}] ${contractName}.cash:${log.line} ${decodedLogData.join(' ')}`); + const sourceName = log.sourceFile + ? basename(log.sourceFile) + : getFrameSourceName(artifact, log.frameId, log.frameBytecode); + console.log(`[Input #${inputIndex}] ${sourceName}:${log.line} ${decodedLogData.join(' ')}`); }; const decodeLogDataEntry = ( dataEntry: LogData, reversedPriorDebugSteps: AuthenticationProgramStateCommon[], vm: VM, + fallbackFrameId: string | undefined, + fallbackFrameBytecode: string | undefined, + frameBytecodeCache: WeakMap, ): string | bigint | boolean => { if (typeof dataEntry === 'string') return dataEntry; - const dataEntryDebugStep = reversedPriorDebugSteps.find((step) => step.ip === dataEntry.ip); + const dataEntryDebugStep = reversedPriorDebugSteps.find((step) => ( + step.ip === dataEntry.ip + && matchesDebugFrame( + step, + dataEntry.frameId ?? fallbackFrameId, + dataEntry.frameBytecode ?? fallbackFrameBytecode, + frameBytecodeCache, + ) + )); if (!dataEntryDebugStep) { throw new Error(`Should not happen: corresponding data entry debug step not found for entry at ip ${dataEntry.ip}`); @@ -259,6 +351,95 @@ const decodeLogDataEntry = ( return decodeStackItem(dataEntry, transformedDebugStep.stack); }; +const matchesDebugFrame = ( + debugStep: AuthenticationProgramStateCommon, + expectedFrameId: string | undefined, + expectedFrameBytecode: string | undefined, + cache: WeakMap, +): boolean => { + if (expectedFrameId && !expectedFrameBytecode) { + return getFrameId(debugStep) === expectedFrameId; + } + + const actualFrameBytecode = getFrameBytecode(debugStep, cache); + if (!expectedFrameBytecode) return true; + + return actualFrameBytecode === expectedFrameBytecode || actualFrameBytecode.endsWith(expectedFrameBytecode); +}; + +const getFrameBytecode = ( + debugStep: AuthenticationProgramStateCommon, + cache: WeakMap, +): string => { + let actualFrameBytecode = cache.get(debugStep.instructions); + if (!actualFrameBytecode) { + actualFrameBytecode = instructionsToBytecodeHex(debugStep.instructions); + cache.set(debugStep.instructions, actualFrameBytecode); + } + + return actualFrameBytecode; +}; + +const getFrameId = (debugStep: AuthenticationProgramStateCommon): string => { + const activeBytecode = binToHex(Uint8Array.from(debugStep.instructions.flatMap((instruction) => ( + Array.from(encodeAuthenticationInstruction(instruction)) + )))); + const matchingEntry = Object.entries(debugStep.functionTable ?? {}).find(([, functionBody]) => ( + binToHex(functionBody) === activeBytecode + )); + + return matchingEntry ? `fn:${matchingEntry[0]}` : '__root__'; +}; + +const findNestedFinalRequireBeforeFinalVerify = ( + executedDebugSteps: AuthenticationProgramStateCommon[], + requireStatements: readonly RequireStatement[], + cache: WeakMap, +): RequireStatement | undefined => { + const finalStep = executedDebugSteps[executedDebugSteps.length - 1]; + if (!finalStep) return undefined; + + const finalFrameId = getFrameId(finalStep); + const finalFrameBytecode = getFrameBytecode(finalStep, cache); + + for (let index = executedDebugSteps.length - 2; index >= 0; index -= 1) { + const step = executedDebugSteps[index]!; + if (matchesDebugFrame(step, finalFrameId, finalFrameBytecode, cache)) continue; + + const matchingRequire = requireStatements.find((statement) => ( + (statement.ip === step.ip || statement.ip === step.ip + 1) + && matchesDebugFrame(step, statement.frameId, statement.frameBytecode, cache) + )); + + if (!matchingRequire) continue; + + const returnedToFinalFrameOnly = executedDebugSteps + .slice(index + 1) + .every((laterStep) => matchesDebugFrame(laterStep, finalFrameId, finalFrameBytecode, cache)); + + if (returnedToFinalFrameOnly) { + return matchingRequire; + } + } + + return undefined; +}; + +const getFrameSourceName = (artifact: Artifact, frameId?: string, frameBytecode?: string): string => { + const matchingFrame = artifact.debug?.frames?.find((frame) => ( + (frameId && frame.id === frameId) + || (frameBytecode && (frame.bytecode === frameBytecode || frame.bytecode.endsWith(frameBytecode))) + )); + + if (matchingFrame?.sourceFile) { + return basename(matchingFrame.sourceFile); + } + + return `${artifact.contractName}.cash`; +}; + +const basename = (sourceFile: string): string => sourceFile.split(/[/\\]/).at(-1) ?? sourceFile; + const applyStackItemTransformations = ( element: StackItem, debugStep: AuthenticationProgramStateCommon, diff --git a/packages/cashscript/src/libauth-template/LibauthTemplate.ts b/packages/cashscript/src/libauth-template/LibauthTemplate.ts index 683fc3a4..4f21e051 100644 --- a/packages/cashscript/src/libauth-template/LibauthTemplate.ts +++ b/packages/cashscript/src/libauth-template/LibauthTemplate.ts @@ -34,8 +34,8 @@ import SignatureTemplate from '../SignatureTemplate.js'; import { addressToLockScript, extendedStringify, zip } from '../utils.js'; import { TransactionBuilder } from '../TransactionBuilder.js'; import { zlibSync } from 'fflate'; -import MockNetworkProvider from '../network/MockNetworkProvider.js'; import { addHexPrefixExceptEmpty, DEFAULT_VM_TARGET, formatBytecodeForDebugging, formatParametersForDebugging, getLockScriptName, getSignatureAndPubkeyFromP2PKHInput, getUnlockScriptName, serialiseTokenDetails } from './utils.js'; +import { VmTarget } from '../interfaces.js'; // TODO: Add / improve descriptions throughout the template generation @@ -47,9 +47,33 @@ export const getLibauthTemplate = ( throw new Error('Cannot use debugging functionality with a transaction that contains custom unlockers'); } - const vmTarget = transactionBuilder.provider instanceof MockNetworkProvider - ? transactionBuilder.provider.vmTarget - : DEFAULT_VM_TARGET; + const requiredArtifactVmTargets = Array.from(new Set( + transactionBuilder.inputs + .flatMap((input) => isContractUnlocker(input.unlocker) + ? [input.unlocker.contract.artifact.compiler.target] + : []) + .filter((target): target is VmTarget => Boolean(target)), + )); + + if (requiredArtifactVmTargets.length > 1) { + throw new Error(`Cannot build a transaction template for contracts requiring different VM targets: ${requiredArtifactVmTargets.join(', ')}`); + } + + const requiredArtifactVmTarget = requiredArtifactVmTargets[0]; + const providerVmTarget = transactionBuilder.provider.vmTarget; + + if ( + requiredArtifactVmTarget + && providerVmTarget + && providerVmTarget !== requiredArtifactVmTarget + && providerVmTarget !== VmTarget.BCH_SPEC + ) { + throw new Error(`Contract artifact requires VM target ${requiredArtifactVmTarget}, but the current provider is configured for ${providerVmTarget}`); + } + + const vmTarget = requiredArtifactVmTarget + ?? providerVmTarget + ?? DEFAULT_VM_TARGET; const template: WalletTemplate = { $schema: 'https://ide.bitauth.com/authentication-template-v0.schema.json', diff --git a/packages/cashscript/src/libauth-template/utils.ts b/packages/cashscript/src/libauth-template/utils.ts index d03191ca..a7ee2966 100644 --- a/packages/cashscript/src/libauth-template/utils.ts +++ b/packages/cashscript/src/libauth-template/utils.ts @@ -1,4 +1,11 @@ -import { AbiFunction, AbiInput, Artifact, bytecodeToScript, formatBitAuthScript } from '@cashscript/utils'; +import { + AbiFunction, + AbiInput, + Artifact, + bytecodeToBitAuthAsm, + bytecodeToScript, + formatBitAuthScript, +} from '@cashscript/utils'; import { HashType, LibauthTokenDetails, SignatureAlgorithm, TokenDetails, VmTarget } from '../interfaces.js'; import { hexToBin, binToHex, isHex, decodeCashAddress, Input, assertSuccess, decodeAuthenticationInstructions, AuthenticationInstructionPush } from '@bitauth/libauth'; import { EncodedFunctionArgument } from '../Argument.js'; @@ -80,6 +87,15 @@ export const formatBytecodeForDebugging = (artifact: Artifact): string => { .join('\n'); } + const usesUnstableControlFlowFormatting = [ + 'OP_DEFINE', + 'OP_INVOKE', + ].some((opcode) => artifact.bytecode.includes(opcode)); + + if (usesUnstableControlFlowFormatting) { + return bytecodeToBitAuthAsm(hexToBin(artifact.debug.bytecode)); + } + return formatBitAuthScript( bytecodeToScript(hexToBin(artifact.debug.bytecode)), artifact.debug.sourceMap, diff --git a/packages/cashscript/src/network/BitcoinRpcNetworkProvider.ts b/packages/cashscript/src/network/BitcoinRpcNetworkProvider.ts index 4ef55820..80ac032f 100644 --- a/packages/cashscript/src/network/BitcoinRpcNetworkProvider.ts +++ b/packages/cashscript/src/network/BitcoinRpcNetworkProvider.ts @@ -1,5 +1,6 @@ -import { Utxo, Network } from '../interfaces.js'; +import { Utxo, Network, VmTarget } from '../interfaces.js'; import NetworkProvider from './NetworkProvider.js'; +import { DEFAULT_VM_TARGET } from '../libauth-template/utils.js'; import { BchnRpcClient, type GetBlockCount, @@ -10,6 +11,7 @@ import { export default class BitcoinRpcNetworkProvider implements NetworkProvider { private rpcClient: BchnRpcClient; + public vmTarget: VmTarget; constructor( public network: Network, @@ -17,9 +19,12 @@ export default class BitcoinRpcNetworkProvider implements NetworkProvider { opts: { rpcUser: string; rpcPassword: string; + vmTarget?: VmTarget; }, ) { - this.rpcClient = new BchnRpcClient({ url, ...opts }); + const { rpcUser, rpcPassword } = opts; + this.rpcClient = new BchnRpcClient({ url, rpcUser, rpcPassword }); + this.vmTarget = opts.vmTarget ?? DEFAULT_VM_TARGET; } async getUtxos(address: string): Promise { diff --git a/packages/cashscript/src/network/ElectrumNetworkProvider.ts b/packages/cashscript/src/network/ElectrumNetworkProvider.ts index 927c8c79..31fbbdb9 100644 --- a/packages/cashscript/src/network/ElectrumNetworkProvider.ts +++ b/packages/cashscript/src/network/ElectrumNetworkProvider.ts @@ -5,9 +5,10 @@ import { type RequestResponse, type ElectrumClientEvents, } from '@electrum-cash/network'; -import { Utxo, Network } from '../interfaces.js'; +import { Utxo, Network, VmTarget } from '../interfaces.js'; import NetworkProvider from './NetworkProvider.js'; import { addressToLockScript } from '../utils.js'; +import { DEFAULT_VM_TARGET } from '../libauth-template/utils.js'; import { NetworkProviderMissingInputsError, NetworkProviderMempoolConflictError, @@ -19,6 +20,7 @@ import { interface OptionsBase { manualConnectionManagement?: boolean; + vmTarget?: VmTarget; } interface CustomHostNameOptions extends OptionsBase { @@ -35,10 +37,12 @@ export default class ElectrumNetworkProvider implements NetworkProvider { private electrum: ElectrumClient; private concurrentRequests: number = 0; private manualConnectionManagement: boolean; + public vmTarget: VmTarget; constructor(public network: Network = Network.MAINNET, options: Options = {}) { this.electrum = this.instantiateElectrumClient(network, options); this.manualConnectionManagement = options?.manualConnectionManagement ?? false; + this.vmTarget = options.vmTarget ?? DEFAULT_VM_TARGET; } private instantiateElectrumClient(network: Network, options: Options): ElectrumClient { diff --git a/packages/cashscript/src/network/FullStackNetworkProvider.ts b/packages/cashscript/src/network/FullStackNetworkProvider.ts index 80d289c8..cc0e8314 100644 --- a/packages/cashscript/src/network/FullStackNetworkProvider.ts +++ b/packages/cashscript/src/network/FullStackNetworkProvider.ts @@ -1,5 +1,6 @@ -import { Utxo, Network } from '../interfaces.js'; +import { Utxo, Network, VmTarget } from '../interfaces.js'; import NetworkProvider from './NetworkProvider.js'; +import { DEFAULT_VM_TARGET } from '../libauth-template/utils.js'; export default class FullStackNetworkProvider implements NetworkProvider { /** @@ -10,10 +11,15 @@ export default class FullStackNetworkProvider implements NetworkProvider { * apiToken: 'eyJhbGciO...' // Your JWT token here. * }) */ + public vmTarget: VmTarget; + constructor( public network: Network, private bchjs: BCHJS, - ) { } + vmTarget: VmTarget = DEFAULT_VM_TARGET, + ) { + this.vmTarget = vmTarget; + } async getUtxos(address: string): Promise { const result = await this.bchjs.Electrumx.utxo(address); diff --git a/packages/cashscript/src/network/NetworkProvider.ts b/packages/cashscript/src/network/NetworkProvider.ts index e966982b..b6801eaa 100644 --- a/packages/cashscript/src/network/NetworkProvider.ts +++ b/packages/cashscript/src/network/NetworkProvider.ts @@ -1,4 +1,4 @@ -import { Utxo, Network } from '../interfaces.js'; +import { Utxo, Network, VmTarget } from '../interfaces.js'; export default interface NetworkProvider { /** @@ -6,6 +6,12 @@ export default interface NetworkProvider { */ network: Network; + /** + * Optional VM target metadata used for local validation/debugging. + * Providers that know which BCH VM rules they target should expose this. + */ + vmTarget?: VmTarget; + /** * Retrieve all UTXOs (confirmed and unconfirmed) for a given address. * @param address The CashAddress for which we wish to retrieve UTXOs. diff --git a/packages/cashscript/test/Contract.test.ts b/packages/cashscript/test/Contract.test.ts index 80d52639..55880747 100644 --- a/packages/cashscript/test/Contract.test.ts +++ b/packages/cashscript/test/Contract.test.ts @@ -1,5 +1,6 @@ import { decodeTransactionUnsafe, hexToBin } from '@bitauth/libauth'; import { placeholder } from '@cashscript/utils'; +import { compileString } from 'cashc'; import { Contract, ElectrumNetworkProvider, @@ -8,6 +9,7 @@ import { randomUtxo, SignatureTemplate, TransactionBuilder, + VmTarget, } from '../src/index.js'; import { aliceAddress, @@ -21,6 +23,79 @@ import mecenasArtifact from './fixture/mecenas.artifact.js'; import deprecatedMecenasArtifact from './fixture/deprecated/mecenas-v0.6.0.json' with { type: 'json' }; import boundedBytesArtifact from './fixture/bounded_bytes.artifact.js'; +const helperFunctionArtifact = compileString(` +contract HelperFunctions() { + function spend(int value) public { + require(isAtLeastSeven(value)); + } + + function isAtLeastSeven(int value) internal { + require(value >= 7); + } +} +`); + +const nestedHelperFunctionArtifact = compileString(` +contract NestedHelperFunctions() { + function spend(int value) public { + require(isPositiveAndEven(value)); + } + + function spendPlusOne(int value) public { + require(isPositiveAndEven(value + 1)); + } + + function isPositiveAndEven(int value) internal { + require(isPositive(value)); + require(isEven(value)); + } + + function isPositive(int value) internal { + require(value > 0); + } + + function isEven(int value) internal { + require(value % 2 == 0); + } + + function unused(int value) internal { + require(value == 42); + } +} +`); + +const oldHelperFunctionArtifact = { + ...helperFunctionArtifact, + compiler: { + ...helperFunctionArtifact.compiler, + target: undefined, + }, +}; + +const zeroArgHelperArtifact = compileString(` +contract ZeroArgHelper() { + function spend() public { + require(exactlyOneOutput()); + } + + function exactlyOneOutput() internal { + require(tx.outputs.length == 1); + } +} +`); + +const publicFunctionCallArtifact = compileString(` +contract PublicFunctionCalls() { + function spend(int value) public { + require(validate(value)); + } + + function validate(int value) public { + require(value == 7); + } +} +`); + describe('Contract', () => { describe('new', () => { it('should fail with incorrect constructor args', () => { @@ -196,4 +271,178 @@ describe('Contract', () => { .toEqual(hexToBin('4135fac4118af15e0d66f30548dd0c31e1108f3389af96bb9db4f2305706e18fe52cc7163f6440fae98c48332d09c30380527a90604f14b4b3fc0c3aa0884c9c0a61210373cc07b54c22da627b572a387a20ea190c9382e5e6d48c1d5b89c5cea2c4c0881914512dbb2c8c02efbac8d92431aa0ac33f6b0bf97078a988ac')); }); }); + + describe('internal functions', () => { + it('exposes only public functions in the ABI and generated unlockers', () => { + const provider = new MockNetworkProvider({ vmTarget: VmTarget.BCH_2026_05 }); + const instance = new Contract(helperFunctionArtifact, [], { provider }); + + expect(helperFunctionArtifact.abi.map((func) => func.name)).toEqual(['spend']); + expect(helperFunctionArtifact.compiler.target).toBe(VmTarget.BCH_2026_05); + expect(helperFunctionArtifact.bytecode).toContain('OP_DEFINE'); + expect(helperFunctionArtifact.bytecode).toContain('OP_INVOKE'); + expect(typeof instance.unlock.spend).toBe('function'); + expect((instance.unlock as Record).isAtLeastSeven).toBeUndefined(); + }); + + it('generates unlockers for internal-function contracts under BCH_2026_05', () => { + const provider = new MockNetworkProvider({ vmTarget: VmTarget.BCH_2026_05 }); + const instance = new Contract(helperFunctionArtifact, [], { provider }); + + const unlocker = instance.unlock.spend(7n); + + expect(unlocker.abiFunction.name).toBe('spend'); + expect(unlocker.params).toEqual([7n]); + expect(instance.address).toContain(':'); + expect(new TransactionBuilder({ provider }) + .addInput(randomUtxo(), unlocker) + .addOutput({ to: instance.address, amount: 1000n }) + .getLibauthTemplate() + .supported[0]).toBe(VmTarget.BCH_2026_05); + }); + + it('can execute an internal-function contract on BCH_2026_05', async () => { + const provider = new MockNetworkProvider({ vmTarget: VmTarget.BCH_2026_05 }); + const instance = new Contract(helperFunctionArtifact, [], { provider }); + const utxo = randomUtxo(); + provider.addUtxo(instance.address, utxo); + + const successTransaction = new TransactionBuilder({ provider }) + .addInput(utxo, instance.unlock.spend(7n)) + .addOutput({ to: instance.address, amount: 1000n }); + + await expect(successTransaction.send()).resolves.toBeDefined(); + + const failingUtxo = randomUtxo(); + provider.addUtxo(instance.address, failingUtxo); + + const failingTransaction = new TransactionBuilder({ provider }) + .addInput(failingUtxo, instance.unlock.spend(6n)) + .addOutput({ to: instance.address, amount: 1000n }); + + await expect(failingTransaction.send()).rejects.toThrow(); + }); + + it('supports nested internal calls shared across multiple public functions', async () => { + const provider = new MockNetworkProvider({ vmTarget: VmTarget.BCH_2026_05 }); + const instance = new Contract(nestedHelperFunctionArtifact, [], { provider }); + + expect(nestedHelperFunctionArtifact.abi.map((func) => func.name)).toEqual(['spend', 'spendPlusOne']); + expect(nestedHelperFunctionArtifact.bytecode.match(/OP_DEFINE/g)).toHaveLength(3); + expect(nestedHelperFunctionArtifact.bytecode.match(/OP_INVOKE/g)).toHaveLength(2); + expect((instance.unlock as Record).isPositiveAndEven).toBeUndefined(); + expect((instance.unlock as Record).unused).toBeUndefined(); + + const successUtxo = randomUtxo(); + provider.addUtxo(instance.address, successUtxo); + + await expect( + new TransactionBuilder({ provider }) + .addInput(successUtxo, instance.unlock.spend(8n)) + .addOutput({ to: instance.address, amount: 1000n }) + .send(), + ).resolves.toBeDefined(); + + const sharedHelperUtxo = randomUtxo(); + provider.addUtxo(instance.address, sharedHelperUtxo); + + await expect( + new TransactionBuilder({ provider }) + .addInput(sharedHelperUtxo, instance.unlock.spendPlusOne(3n)) + .addOutput({ to: instance.address, amount: 1000n }) + .send(), + ).resolves.toBeDefined(); + }); + + it('supports zero-argument internal functions', async () => { + const provider = new MockNetworkProvider({ vmTarget: VmTarget.BCH_2026_05 }); + const instance = new Contract(zeroArgHelperArtifact, [], { provider }); + const utxo = randomUtxo(); + provider.addUtxo(instance.address, utxo); + + await expect( + new TransactionBuilder({ provider }) + .addInput(utxo, instance.unlock.spend()) + .addOutput({ to: instance.address, amount: 1000n }) + .send(), + ).resolves.toBeDefined(); + }); + + it('supports invoking a public function from another public function while keeping both ABI methods', async () => { + const provider = new MockNetworkProvider({ vmTarget: VmTarget.BCH_2026_05 }); + const instance = new Contract(publicFunctionCallArtifact, [], { provider }); + + expect(publicFunctionCallArtifact.abi.map((func) => func.name)).toEqual(['spend', 'validate']); + + const utxo = randomUtxo(); + provider.addUtxo(instance.address, utxo); + + await expect( + new TransactionBuilder({ provider }) + .addInput(utxo, instance.unlock.spend(7n)) + .addOutput({ to: instance.address, amount: 1000n }) + .send(), + ).resolves.toBeDefined(); + }); + + it('fails nested internal-function validation through the SDK when requirements are not met', async () => { + const provider = new MockNetworkProvider({ vmTarget: VmTarget.BCH_2026_05 }); + const instance = new Contract(nestedHelperFunctionArtifact, [], { provider }); + const utxo = randomUtxo(); + provider.addUtxo(instance.address, utxo); + + await expect( + new TransactionBuilder({ provider }) + .addInput(utxo, instance.unlock.spend(3n)) + .addOutput({ to: instance.address, amount: 1000n }) + .send(), + ).rejects.toThrow(); + }); + + it('fails fast when a provider VM target does not match the artifact requirement', () => { + const provider = new MockNetworkProvider({ vmTarget: VmTarget.BCH_2025_05 }); + + expect(() => new Contract(helperFunctionArtifact, [], { provider })) + .toThrow(/requires VM target BCH_2026_05/); + }); + + it('fails fast when an electrum provider VM target does not match the artifact requirement', () => { + const provider = new ElectrumNetworkProvider(Network.CHIPNET, { vmTarget: VmTarget.BCH_2025_05 }); + + expect(() => new Contract(helperFunctionArtifact, [], { provider })) + .toThrow(/requires VM target BCH_2026_05/); + }); + + it('accepts older artifacts that do not encode compiler.target metadata', () => { + const provider = new MockNetworkProvider({ vmTarget: VmTarget.BCH_2026_05 }); + + expect(() => new Contract(oldHelperFunctionArtifact, [], { provider })).not.toThrow(); + }); + + it('fails when mixing contracts that require different VM targets in one transaction template', () => { + const provider = new MockNetworkProvider({ vmTarget: VmTarget.BCH_SPEC }); + const helperInstance = new Contract(helperFunctionArtifact, [], { provider }); + const legacyTargetArtifact = { + ...helperFunctionArtifact, + contractName: 'LegacyTargetHelper', + compiler: { + ...helperFunctionArtifact.compiler, + target: VmTarget.BCH_2025_05, + }, + }; + const legacyInstance = new Contract(legacyTargetArtifact, [], { provider }); + + const utxo1 = randomUtxo(); + const utxo2 = randomUtxo(); + provider.addUtxo(helperInstance.address, utxo1); + provider.addUtxo(legacyInstance.address, utxo2); + + expect(() => new TransactionBuilder({ provider }) + .addInput(utxo1, helperInstance.unlock.spend(7n)) + .addInput(utxo2, legacyInstance.unlock.spend(7n)) + .addOutput({ to: helperInstance.address, amount: 1000n }) + .getLibauthTemplate()) + .toThrow(/different VM targets/); + }); + }); }); diff --git a/packages/cashscript/test/debugging.test.ts b/packages/cashscript/test/debugging.test.ts index 2b27b7a0..f4fdcd0e 100644 --- a/packages/cashscript/test/debugging.test.ts +++ b/packages/cashscript/test/debugging.test.ts @@ -1,4 +1,5 @@ import { Contract, FailedTransactionError, MockNetworkProvider, SignatureAlgorithm, SignatureTemplate, TransactionBuilder, VmTarget } from '../src/index.js'; +import { compileString } from 'cashc'; import { DEFAULT_VM_TARGET } from '../src/libauth-template/utils.js'; import { aliceAddress, alicePriv, alicePub, bobPriv, bobPub } from './fixture/vars.js'; import { randomUtxo } from '../src/utils.js'; @@ -14,6 +15,7 @@ import { artifactTestZeroHandling, artifactTestRequireInsideLoop, artifactTestLogInsideLoop, + artifactTestInternalFunctions, } from './fixture/debugging/debugging_contracts.js'; import { sha256 } from '@cashscript/utils'; @@ -28,6 +30,9 @@ describe('Debugging tests', () => { const contractTestLogInsideLoop = new Contract(artifactTestLogInsideLoop, [], { provider }); const contractTestLogInsideLoopUtxo = randomUtxo(); provider.addUtxo(contractTestLogInsideLoop.address, contractTestLogInsideLoopUtxo); + const contractTestInternalFunctions = new Contract(artifactTestInternalFunctions, [], { provider }); + const contractTestInternalFunctionsUtxo = randomUtxo(); + provider.addUtxo(contractTestInternalFunctions.address, contractTestInternalFunctionsUtxo); it('should log correct values', async () => { const transaction = new TransactionBuilder({ provider }) @@ -213,6 +218,14 @@ describe('Debugging tests', () => { }); it.todo('should log intermediate results that get optimised out inside a loop'); + + it('should log from an internal function with the correct source line', async () => { + const transaction = new TransactionBuilder({ provider }) + .addInput(contractTestInternalFunctionsUtxo, contractTestInternalFunctions.unlock.test_internal_logs(2n)) + .addOutput({ to: contractTestInternalFunctions.address, amount: 10000n }); + + expect(transaction).toLog(new RegExp('^\\[Input #0] Test.cash:12 internal value: 2$')); + }); }); describe('require statements', () => { @@ -228,6 +241,9 @@ describe('Debugging tests', () => { const contractTestRequireInsideLoop = new Contract(artifactTestRequireInsideLoop, [], { provider }); const contractTestRequireInsideLoopUtxo = randomUtxo(); provider.addUtxo(contractTestRequireInsideLoop.address, contractTestRequireInsideLoopUtxo); + const contractTestInternalFunctions = new Contract(artifactTestInternalFunctions, [], { provider }); + const contractTestInternalFunctionsUtxo = randomUtxo(); + provider.addUtxo(contractTestInternalFunctions.address, contractTestInternalFunctionsUtxo); // test_require it('should fail with error message when require statement fails in a multi-function contract', async () => { @@ -265,6 +281,42 @@ describe('Debugging tests', () => { expect(transaction).not.toFailRequireWith(/1 should equal 1/); }); + it('should fail with the internal function require message and statement', async () => { + const transaction = new TransactionBuilder({ provider }) + .addInput(contractTestInternalFunctionsUtxo, contractTestInternalFunctions.unlock.test_internal_require(0n)) + .addOutput({ to: contractTestInternalFunctions.address, amount: 1000n }); + + expect(transaction).toFailRequireWith('Test.cash:17 Require statement failed at input 0 in contract Test.cash at line 17 with the following message: internal value should be 1.'); + expect(transaction).toFailRequireWith('Failing statement: require(value == 1, \'internal value should be 1\')'); + }); + + it('should not misattribute a nested non-require helper failure to an earlier helper require', async () => { + const artifact = compileString(` +contract Test() { + function trigger() public { + require(runInternalFailure()); + } + + function runInternalFailure() internal { + require(true, 'earlier require should not be reported'); + int value = tx.inputs[5].value; + require(value == 1); + } +} +`); + const contract = new Contract(artifact, [], { provider }); + const utxo = randomUtxo(); + provider.addUtxo(contract.address, utxo); + + const transaction = new TransactionBuilder({ provider }) + .addInput(utxo, contract.unlock.trigger()) + .addOutput({ to: contract.address, amount: 1000n }); + + expect(() => transaction.debug()).toThrow(`Reason: ${AuthenticationErrorCommon.invalidTransactionUtxoIndex}`); + expect(() => transaction.debug()).toThrow('Failing statement: tx.inputs[5].value'); + expect(() => transaction.debug()).not.toThrow(/earlier require should not be reported/); + }); + // test_multiple_require_statements_final_fails it('it should only fail with correct error message when there are multiple require statements where the final statement fails', async () => { const transaction = new TransactionBuilder({ provider }) diff --git a/packages/cashscript/test/fixture/debugging/debugging_contracts.ts b/packages/cashscript/test/fixture/debugging/debugging_contracts.ts index be048ef2..00edd56b 100644 --- a/packages/cashscript/test/fixture/debugging/debugging_contracts.ts +++ b/packages/cashscript/test/fixture/debugging/debugging_contracts.ts @@ -2,44 +2,44 @@ import { compileString } from 'cashc'; const CONTRACT_TEST_REQUIRES = ` contract Test() { - function test_logs() { + function test_logs() public { console.log("Hello World"); require(1 == 2); } - function test_no_logs() { + function test_no_logs() public { require(1 == 2); } - function test_require() { + function test_require() public { require(1 == 2, "1 should equal 2"); } - function test_require_no_failure() { + function test_require_no_failure() public { require(1 == 1, "1 should equal 1"); } - function test_multiple_require_statements() { + function test_multiple_require_statements() public { require(1 == 2, "1 should equal 2"); require(1 == 1, "1 should equal 1"); } - function test_multiple_require_statements_final_fails() { + function test_multiple_require_statements_final_fails() public { require(1 == 1, "1 should equal 1"); require(1 == 2, "1 should equal 2"); } - function test_multiple_require_statements_no_message_final() { + function test_multiple_require_statements_no_message_final() public { require(1 == 1, "1 should equal 1"); require(1 == 2); } - function test_timeops_as_final_require() { + function test_timeops_as_final_require() public { require(1 == 1, "1 should equal 1"); require(tx.time >= 100000000, "time should be HUGE"); } - function test_final_require_in_if_statement(int switch) { + function test_final_require_in_if_statement(int switch) public { if (switch == 1) { int a = 2; require(1 == a, "1 should equal 2"); @@ -52,7 +52,7 @@ contract Test() { } } - function test_final_require_in_if_statement_with_deep_reassignment() { + function test_final_require_in_if_statement_with_deep_reassignment() public { int a = 0; int b = 1; int c = 2; @@ -65,30 +65,30 @@ contract Test() { } } - function test_invalid_split_range() { + function test_invalid_split_range() public { bytes test = 0x1234; bytes test2 = test.split(4)[0]; require(test2 == 0x1234); } - function test_invalid_input_index() { + function test_invalid_input_index() public { require(tx.inputs[5].value == 1000); } - function test_fail_checksig(sig s, pubkey pk) { + function test_fail_checksig(sig s, pubkey pk) public { require(checkSig(s, pk), "Signatures do not match"); require(1 == 2, "1 should equal 2"); } - function test_fail_checksig_final_verify(sig s, pubkey pk) { + function test_fail_checksig_final_verify(sig s, pubkey pk) public { require(checkSig(s, pk), "Signatures do not match"); } - function test_fail_checkdatasig(datasig s, bytes message, pubkey pk) { + function test_fail_checkdatasig(datasig s, bytes message, pubkey pk) public { require(checkDataSig(s, message, pk), "Data Signatures do not match"); } - function test_fail_checkmultisig(sig s1, pubkey pk1, sig s2, pubkey pk2) { + function test_fail_checkmultisig(sig s1, pubkey pk1, sig s2, pubkey pk2) public { require(checkMultiSig([s1, s2], [pk1, pk2]), "Multi Signatures do not match"); } } @@ -96,7 +96,7 @@ contract Test() { const CONTRACT_TEST_REQUIRE_INSIDE_LOOP = ` contract Test() { - function test_require_inside_loop() { + function test_require_inside_loop() public { int i = 0; do { i = i + 1; @@ -104,7 +104,7 @@ contract Test() { } while (i < 10); } - function test_require_inside_while_loop() { + function test_require_inside_while_loop() public { int i = 0; while (i < 10) { @@ -115,7 +115,7 @@ contract Test() { require(i == 10); } - function test_require_inside_for_loop() { + function test_require_inside_for_loop() public { int i = 0; for (i = 0; i < 4; i = i + 1) { @@ -125,7 +125,7 @@ contract Test() { require(i == 4); } - function test_require_inside_loop_complex() { + function test_require_inside_loop_complex() public { int i = 0; do { @@ -147,7 +147,7 @@ contract Test() { const CONTRACT_TEST_REQUIRE_SINGLE_FUNCTION = ` contract Test() { - function test_require_single_function() { + function test_require_single_function() public { require(tx.outputs.length == 1, "should have 1 output"); } }`; @@ -155,7 +155,7 @@ contract Test() { const CONTRACT_TEST_MULTILINE_REQUIRES = ` contract Test() { // We test this because the cleanup looks different and the final OP_VERIFY isn't removed for these kinds of functions - function test_fail_large_cleanup() { + function test_fail_large_cleanup() public { int a = 1; int b = 2; int c = 3; @@ -176,7 +176,7 @@ contract Test() { require(1 == 2, "1 should equal 2"); } - function test_fail_multiline_require() { + function test_fail_multiline_require() public { require( 1 == 2, "1 should equal 2" @@ -185,14 +185,14 @@ contract Test() { require(1 == 1); } - function test_fail_multiline_final_require() { + function test_fail_multiline_final_require() public { require( 1 == 2, "1 should equal 2" ); } - function test_multiline_non_require_error() { + function test_multiline_non_require_error() public { int x = tx.outputs[ 5 @@ -201,7 +201,7 @@ contract Test() { require(x == 1000); } - function test_multiline_require_with_unary_op() { + function test_multiline_require_with_unary_op() public { require( !( 0x000000 @@ -219,7 +219,7 @@ contract Test() { require(1 == 1); } - function test_multiline_require_with_instantiation() { + function test_multiline_require_with_instantiation() public { require( new LockingBytecodeP2PKH( hash160(0x000000) @@ -238,7 +238,7 @@ contract Test() { const CONTRACT_TEST_ZERO_HANDLING = ` contract Test(int a) { - function test_zero_handling(int b) { + function test_zero_handling(int b) public { require(a == 0, "a should be 0"); require(b == 0, "b should be 0"); require(a == b, "a should equal b"); @@ -248,7 +248,7 @@ contract Test(int a) { const CONTRACT_TEST_LOGS = ` contract Test(pubkey owner) { - function transfer(sig ownerSig, int num) { + function transfer(sig ownerSig, int num) public { console.log('Hello First Function'); require(checkSig(ownerSig, owner)); @@ -260,12 +260,12 @@ contract Test(pubkey owner) { require(num == 1000); } - function secondFunction() { + function secondFunction() public { console.log("Hello Second Function"); require(1 == 1); } - function functionWithIfStatement(int a) { + function functionWithIfStatement(int a) public { int b = 0; if (a == 1) { @@ -282,11 +282,11 @@ contract Test(pubkey owner) { require(1 == 1); } - function noLogs() { + function noLogs() public { require(1 == 1); } - function test_log_intermediate_result() { + function test_log_intermediate_result() public { bytes32 singleHash = sha256(owner); console.log(singleHash); @@ -295,7 +295,7 @@ contract Test(pubkey owner) { require(doubleHash.length == 32, "doubleHash should be 32 bytes"); } - function test_log_inside_notif_statement(bool isLastScriptHash) { + function test_log_inside_notif_statement(bool isLastScriptHash) public { int inputValue = tx.inputs[this.activeInputIndex].value; console.log('before:', inputValue); if (!isLastScriptHash) { @@ -308,7 +308,7 @@ contract Test(pubkey owner) { const CONTRACT_TEST_LOG_INSIDE_LOOP = ` contract Test() { - function test_log_inside_loop() { + function test_log_inside_loop() public { int i = 0; do { console.log('i:', i); @@ -318,7 +318,7 @@ contract Test() { require(i == 10); } - function test_log_inside_while_loop() { + function test_log_inside_while_loop() public { int i = 0; while (i < 3) { @@ -329,7 +329,7 @@ contract Test() { require(i == 3); } - function test_log_inside_for_loop() { + function test_log_inside_for_loop() public { int sum = 0; for (int i = 0; i < 3; i = i + 1) { @@ -340,7 +340,7 @@ contract Test() { require(sum == 3); } - function test_log_inside_loop_complex() { + function test_log_inside_loop_complex() public { int i = 0; int l = 5; @@ -371,7 +371,7 @@ contract Test() { const CONTRACT_TEST_CONSECUTIVE_LOGS = ` contract Test(pubkey owner) { - function transfer(sig ownerSig, int num) { + function transfer(sig ownerSig, int num) public { require(checkSig(ownerSig, owner)); bytes2 beef = 0xbeef; @@ -387,7 +387,7 @@ contract Test(pubkey owner) { const CONTRACT_TEST_MULTIPLE_LOGS = ` contract Test(pubkey owner) { - function transfer(sig ownerSig, int num) { + function transfer(sig ownerSig, int num) public { require(checkSig(ownerSig, owner)); console.log(ownerSig, owner, num); @@ -404,7 +404,7 @@ contract Test(pubkey owner) { const CONTRACT_TEST_MULTIPLE_CONSTRUCTOR_PARAMETERS = ` contract Test(pubkey owner, int num, int num2, int num3, int num4, int num5) { - function transfer(sig ownerSig) { + function transfer(sig ownerSig) public { console.log('Hello First Function'); require(checkSig(ownerSig, owner)); @@ -420,13 +420,34 @@ contract Test(pubkey owner, int num, int num2, int num3, int num4, int num5) { require(num5 == 5000); } - function secondFunction() { + function secondFunction() public { console.log("Hello Second Function"); require(1 == 1); } } `; +const CONTRACT_TEST_INTERNAL_FUNCTIONS = ` +contract Test() { + function test_internal_logs(int value) public { + require(runInternalLog(value)); + } + + function test_internal_require(int value) public { + require(runInternalRequire(value)); + } + + function runInternalLog(int value) internal { + console.log('internal value:', value); + require(value >= 0, 'value should be non-negative'); + } + + function runInternalRequire(int value) internal { + require(value == 1, 'internal value should be 1'); + } +} +`; + export const artifactTestRequires = compileString(CONTRACT_TEST_REQUIRES); export const artifactTestSingleFunction = compileString(CONTRACT_TEST_REQUIRE_SINGLE_FUNCTION); export const artifactTestMultilineRequires = compileString(CONTRACT_TEST_MULTILINE_REQUIRES); @@ -437,3 +458,4 @@ export const artifactTestMultipleLogs = compileString(CONTRACT_TEST_MULTIPLE_LOG export const artifactTestMultipleConstructorParameters = compileString(CONTRACT_TEST_MULTIPLE_CONSTRUCTOR_PARAMETERS); export const artifactTestRequireInsideLoop = compileString(CONTRACT_TEST_REQUIRE_INSIDE_LOOP); export const artifactTestLogInsideLoop = compileString(CONTRACT_TEST_LOG_INSIDE_LOOP); +export const artifactTestInternalFunctions = compileString(CONTRACT_TEST_INTERNAL_FUNCTIONS); diff --git a/packages/cashscript/test/fixture/debugging/multi_contract_debugging_contracts.ts b/packages/cashscript/test/fixture/debugging/multi_contract_debugging_contracts.ts index 412a7b42..ff53f2d3 100644 --- a/packages/cashscript/test/fixture/debugging/multi_contract_debugging_contracts.ts +++ b/packages/cashscript/test/fixture/debugging/multi_contract_debugging_contracts.ts @@ -2,7 +2,7 @@ import { compileString } from 'cashc'; const SAME_NAME_DIFFERENT_PATH = ` contract SameNameDifferentPath(int a) { - function function_1(int b) { + function function_1(int b) public { if (a == 0) { console.log("a is 0"); require(b == 0, "b should be 0"); @@ -16,7 +16,7 @@ contract SameNameDifferentPath(int a) { const NAME_COLLISION = ` contract NameCollision(int a) { - function name_collision(int b) { + function name_collision(int b) public { require(a == 0, "a should be 0"); require(b == 0, "b should be 0"); } @@ -25,7 +25,7 @@ contract NameCollision(int a) { const CONTRACT_NAME_COLLISION = ` contract NameCollision(int a) { - function name_collision(int b) { + function name_collision(int b) public { require(b == 1, "b should be 1"); require(a == 1, "a should be 1"); } @@ -34,7 +34,7 @@ contract NameCollision(int a) { const FUNCTION_NAME_COLLISION = ` contract FunctionNameCollision(int a) { - function name_collision(int b) { + function name_collision(int b) public { require(b == 1, "b should be 1"); require(a == 1, "a should be 1"); } diff --git a/packages/utils/src/artifact.ts b/packages/utils/src/artifact.ts index 1e44ddcd..8620f462 100644 --- a/packages/utils/src/artifact.ts +++ b/packages/utils/src/artifact.ts @@ -1,5 +1,15 @@ +import { LocationI } from './types.js'; + +export type VmTarget = + | 'BCH_2023_05' + | 'BCH_2025_05' + | 'BCH_2026_05' + | 'BCH_SPEC'; + export interface CompilerOptions { enforceFunctionParameterTypes?: boolean; + requireExplicitFunctionVisibility?: boolean; + target?: VmTarget; } export interface AbiInput { @@ -17,13 +27,25 @@ export interface DebugInformation { sourceMap: string; // see documentation for `generateSourceMap` logs: readonly LogEntry[]; // log entries generated from `console.log` statements requires: readonly RequireStatement[]; // messages for failing `require` statements + frames?: readonly DebugFrame[]; // source/provenance details for the root frame and helper frames sourceTags?: string; // semantic tags for opcodes (e.g. loop update/condition ranges) } +export interface DebugFrame { + id: string; + bytecode: string; + sourceMap: string; + source: string; + sourceFile?: string; +} + export interface LogEntry { ip: number; // instruction pointer line: number; // line in the source code data: readonly LogData[]; // data to be logged + frameBytecode?: string; // active bytecode frame in which this log executes + frameId?: string; // compiler-assigned helper/root frame identifier + sourceFile?: string; // source file in which this log executes } export interface StackItem { @@ -33,6 +55,8 @@ export interface StackItem { stackIndex: number; // Instruction pointer at which we can access the logged variable ip: number; + frameBytecode?: string; // active bytecode frame in which this stack item is available + frameId?: string; // compiler-assigned helper/root frame identifier // Operations to apply to the debug state at the specified instruction pointer to make sure that the variable is // on the correct position on the stack. This is used when we're optimising bytecode where the logged variable is // an intermediate result that existed in the unoptimised bytecode, but no longer exists in the optimised bytecode. @@ -45,6 +69,10 @@ export interface RequireStatement { ip: number; // instruction pointer line: number; // line in the source code message?: string; // custom message for failing `require` statement + frameBytecode?: string; // active bytecode frame in which this require executes + frameId?: string; // compiler-assigned helper/root frame identifier + location?: LocationI; // source location of the full require statement + sourceFile?: string; // source file in which this require executes } export interface Artifact { @@ -57,6 +85,7 @@ export interface Artifact { compiler: { name: string; version: string; + target?: VmTarget; options?: CompilerOptions; } updatedAt: string; diff --git a/packages/utils/src/script.ts b/packages/utils/src/script.ts index d19dd150..646f2ced 100644 --- a/packages/utils/src/script.ts +++ b/packages/utils/src/script.ts @@ -320,6 +320,8 @@ function replaceOps( const scriptIp = scriptIndex + constructorParamLength; newRequires = newRequires.map((require) => { + if (require.frameBytecode) return require; + // We calculate the new ip of the require by subtracting the length diff between the matched pattern and replacement const newCalculatedRequireIp = require.ip - lengthDiff; @@ -332,15 +334,18 @@ function replaceOps( }); newLogs = newLogs.map((log) => { + if (log.frameBytecode) return log; + // We calculate the new ip of the log by subtracting the length diff between the matched pattern and replacement const newCalculatedLogIp = log.ip - lengthDiff; return { + ...log, // If the log is within the pattern, we want to make sure that the new ip is at least the scriptIp ip: log.ip >= scriptIp ? Math.max(scriptIp, newCalculatedLogIp) : log.ip, - line: log.line, data: log.data.map((data) => { if (typeof data === 'string') return data; + if (data.frameBytecode) return data; // If the log is completely before the pattern, we don't need to change anything if (data.ip <= scriptIp) return data; diff --git a/packages/utils/src/types.ts b/packages/utils/src/types.ts index d5a6e52e..5c71e173 100644 --- a/packages/utils/src/types.ts +++ b/packages/utils/src/types.ts @@ -201,6 +201,7 @@ export interface LocationI { line: number, column: number }; + sourceFile?: string; } export type SingleLocationData = { diff --git a/website/docs/compiler/artifacts.md b/website/docs/compiler/artifacts.md index b2f21d22..6627ea65 100644 --- a/website/docs/compiler/artifacts.md +++ b/website/docs/compiler/artifacts.md @@ -13,12 +13,13 @@ Artifacts allow any third-party SDKs to be developed, since these SDKs only need interface Artifact { contractName: string // Contract name constructorInputs: AbiInput[] // Arguments required to instantiate a contract - abi: AbiFunction[] // functions that can be called + abi: AbiFunction[] // Public functions that can be called from the SDK bytecode: string // Compiled Script without constructor parameters added (in ASM format) source: string // Source code of the CashScript contract compiler: { name: string // Compiler used to compile this contract version: string // Compiler version used to compile this contract + target?: string // Optional VM target required by this artifact (e.g. BCH_2026_05) options?: CompilerOptions // Compiler options used to compile this contract } debug?: { @@ -56,10 +57,19 @@ interface StackItem { interface RequireStatement { ip: number; // instruction pointer line: number; // line in the source code - message: string; // custom message for failing `require` statement + message?: string; // custom message for failing `require` statement } interface CompilerOptions { enforceFunctionParameterTypes?: boolean; // Enforce function parameter types (default: true) + target?: 'BCH_2023_05' | 'BCH_2025_05' | 'BCH_2026_05' | 'BCH_SPEC'; // Optional explicit VM target recorded in the artifact metadata } ``` + +:::note +Functions declared `internal` are excluded from the artifact ABI. They can still be called by other CashScript functions, but they are not exposed as public SDK entrypoints. +::: + +:::note +Artifacts using BCH function opcodes record a compatible target of at least `BCH_2026_05`. The compiler rejects lower explicit targets for contracts that require `OP_DEFINE` or `OP_INVOKE`. SDK integrations can use this to validate that their runtime/debug environment matches the contract's required VM semantics. +::: diff --git a/website/docs/compiler/bch-functions.md b/website/docs/compiler/bch-functions.md new file mode 100644 index 00000000..d200b341 --- /dev/null +++ b/website/docs/compiler/bch-functions.md @@ -0,0 +1,167 @@ +--- +title: BCH Internal Functions (beta) +--- + +CashScript supports user-defined internal function calls within a contract by compiling them to BCH function opcodes. + +:::caution +This feature is currently in beta. The visibility syntax and some compilation details may still change in a future release. + +CashScript function calls rely on BCH 2026 function semantics. Teams should only use this feature in environments that support `BCH_2026_05` behavior, and should configure testing/debugging providers accordingly. +::: + +At the Script level, this feature is implemented using: + +- `OP_DEFINE` to register a function body in the function table +- `OP_INVOKE` to execute a previously-defined function + +This page documents how CashScript maps contract functions to that execution model. + +## Overview + +CashScript contract functions now serve two roles: + +- public entrypoints, which appear in the artifact ABI and can be called from the SDK +- internal functions, which can be called by other CashScript functions but are hidden from the ABI + +Public functions can also call other public functions. In that case, the called function remains in the ABI and is also compiled into the BCH function table if it is invoked internally. + +Example: + +```solidity +contract Example() { + function spend(int x) public { + require(isTen(x)); + } + + function isTen(int value) internal { + require(value == 10); + } +} +``` + +In this example: + +- `spend()` is a public function +- `isTen()` is an internal function because it is declared `internal` + +## Internal Functions + +CashScript's execution model supports general internal functions. Visibility is expressed explicitly in function declarations. + +For backward compatibility, omitted visibility currently still defaults to `public` and produces a compiler warning. + +Internal functions: + +- can be called by other functions in the same contract +- are excluded from the artifact ABI +- are not exposed as unlock methods in the TypeScript SDK + +For example, `contract.unlock.isTen` will be unavailable even though `spend()` can still invoke `isTen()`. + +CashScript also rejects user-defined function names that collide with built-in global function names like `sha256` or `checkSig`. + +## Compilation Model + +When a contract contains user-defined function calls: + +1. CashScript computes the closure of all invoked functions. +2. Each function reachable from a public entrypoint through internal calls is compiled into its own bytecode fragment. +3. Those fragments are registered at the beginning of the script using `OP_DEFINE`. +4. When a function call appears in the contract body, CashScript emits `OP_INVOKE`. + +Only functions reachable from public entrypoints are defined. Dead internal-only call chains are not added to the function table. + +Public entrypoint dispatch remains separate from BCH function invocation: + +- public functions are still selected using CashScript's normal ABI function selector logic +- internal user-defined calls use `OP_DEFINE` and `OP_INVOKE` + +## Return Value Semantics + +CashScript internal functions currently return a boolean success value. + +That means user-defined function calls are most naturally used in boolean positions, for example: + +```solidity +require(validateState(expectedHash)); +``` + +Internally, CashScript compiles invoked function bodies so they leave a single boolean result on the stack. + +## Constructor And Parameter Access + +Invoked internal functions can safely access: + +- their own parameters +- global built-in functions and transaction globals + +They cannot reference constructor parameters. CashScript rejects this at compile time. + +## ABI Behavior + +Only public functions are written to the artifact ABI. + +So for: + +```solidity +contract Example() { + function spend(int x) public { + require(isTen(x)); + } + + function isTen(int value) internal { + require(value == 10); + } +} +``` + +the artifact ABI only contains: + +```ts +[ + { name: 'spend', inputs: [{ name: 'x', type: 'int' }] } +] +``` + +## Current Limitations + +There are currently several important restrictions: + +- internally-invoked functions cannot use `checkSig()`, `checkMultiSig()`, or `checkDataSig()` +- internally-invoked functions cannot reference constructor parameters +- recursive or mutually-recursive user-defined function calls are rejected by the compiler + +This restriction exists because signature coverage for invoked bytecode needs additional SDK/compiler metadata work. + +For now, keep signature validation and constructor-parameter-dependent logic in public entrypoint functions, and use internal functions for shared logic that depends only on function arguments and other globals. + +Artifacts using BCH function opcodes record at least `compiler.target: 'BCH_2026_05'`, and the compiler rejects lower explicit targets. The SDK validates this against provider VM target metadata during local testing/debugging. + +For local testing with the SDK, configure your provider explicitly: + +```ts +import { Contract, MockNetworkProvider, VmTarget } from 'cashscript'; + +const provider = new MockNetworkProvider({ vmTarget: VmTarget.BCH_2026_05 }); +const contract = new Contract(artifact, [], { provider }); +``` + +Nested `console.log` and `require(...)` statements inside invoked internal functions are included in CashScript's debug output, including the internal frame's source line and failing `require(...)` statement when available. + +## Compiler Options + +Function visibility is part of the source syntax, so no compiler option is required to decide whether a function is public or internal. + +## When To Use This + +This feature is most useful when: + +- multiple public functions share the same logic +- you want cleaner contract structure without exposing every internal function in the ABI +- you want the compiler to emit reusable BCH function bodies via `OP_DEFINE`/`OP_INVOKE` + +It is less useful when: + +- the internal function performs signature checks +- the logic is only used once and inlining is simpler diff --git a/website/docs/compiler/compiler.md b/website/docs/compiler/compiler.md index cab71eae..6cc29c7b 100644 --- a/website/docs/compiler/compiler.md +++ b/website/docs/compiler/compiler.md @@ -5,6 +5,8 @@ title: Compiler The CashScript compiler is called `cashc` and is used to compile CashScript `.cash` contract files into `.json` (or `.ts`) artifact files. These artifact files can be used to instantiate a CashScript contract with the help of the CashScript SDK. For more information on this artifact format refer to [Artifacts](/docs/compiler/artifacts). +For details on how user-defined function calls compile to BCH `OP_DEFINE` and `OP_INVOKE`, see [BCH Functions (beta)](/docs/compiler/bch-functions). + :::info Because of the separation of the compiler and the SDK, CashScript contracts can be integrated into other programming languages in the future. ::: @@ -37,6 +39,7 @@ Options: -c, --opcount Display the number of opcodes in the compiled bytecode. -s, --size Display the size in bytes of the compiled bytecode. -S, --skip-enforce-function-parameter-types Do not enforce function parameter types. + -t, --target Record a required VM target in the artifact metadata. -f, --format Specify the format of the output. (choices: "json", "ts", default: "json") -?, --help Display help @@ -101,6 +104,7 @@ const P2PKH = compileString(source); ```ts interface CompilerOptions { enforceFunctionParameterTypes?: boolean; + target?: 'BCH_2023_05' | 'BCH_2025_05' | 'BCH_2026_05' | 'BCH_SPEC'; } ``` @@ -109,3 +113,7 @@ The `enforceFunctionParameterTypes` option is used to enforce function parameter If set to `false`, the compiler will not enforce function parameter types. This means that it is possible for `bytes20` values to have a different length at runtime than the expected 20 bytes. Or that `bool` values are not actually booleans, but integers. This option is useful if you are certain that passing in incorrect function parameter types will not cause runtime vulnerabilities, and you want to save on the extra opcodes that are added to the script to enforce the types. + +The `target` option can be used to explicitly record a required VM target in the artifact metadata. In most cases this is inferred automatically when the compiled contract uses BCH function opcodes such as `OP_DEFINE` and `OP_INVOKE`. If your contract requires those opcodes, the compiler will reject explicit targets below `BCH_2026_05`. + +Function visibility is part of the source syntax. During the current transition, omitted visibility still defaults to `public` and produces a compiler warning. diff --git a/website/docs/compiler/grammar.md b/website/docs/compiler/grammar.md index 7a6c4018..14425abb 100644 --- a/website/docs/compiler/grammar.md +++ b/website/docs/compiler/grammar.md @@ -135,7 +135,7 @@ consoleParameterList ; functionCall - : Identifier expressionList // Only built-in functions are accepted + : Identifier expressionList // Built-in and user-defined functions are accepted ; expressionList @@ -294,3 +294,13 @@ LINE_COMMENT : '//' ~[\r\n]* -> channel(HIDDEN) ; ``` + +:::note +User-defined function calls compile to BCH function opcodes. Functions declared `internal` are excluded from the public ABI. +::: + +For the full compilation model, see [BCH Functions (beta)](/docs/compiler/bch-functions). + +:::caution +Internally-invoked functions currently cannot use `checkSig()`, `checkMultiSig()`, or `checkDataSig()`. +::: diff --git a/website/docs/language/contracts.md b/website/docs/language/contracts.md index 98d4fd86..1022a441 100644 --- a/website/docs/language/contracts.md +++ b/website/docs/language/contracts.md @@ -59,9 +59,55 @@ contract TransferWithTimeout(pubkey sender, pubkey recipient, int timeout) { } ``` +### Internal Functions (beta) + +CashScript functions can also call other functions in the same contract. + +:::caution +User-defined internal functions are currently in beta. The visibility syntax and some implementation details may still change in a future release. + +This feature depends on BCH 2026 function semantics. Artifacts for contracts using internal functions record `compiler.target: 'BCH_2026_05'`. When testing or integrating these contracts in apps, make sure your environment is configured for `BCH_2026_05`. +::: + +Visibility is declared explicitly on the function definition: + +- it can still be called from other contract functions +- it is not included in the compiled ABI +- it is therefore not exposed as an SDK unlock method + +For backward compatibility, omitted visibility currently still defaults to `public` and produces a compiler warning. + +This is useful for shared logic that should not appear as a public entrypoint. + +```solidity +contract Vault(pubkey owner) { + function spend(sig ownerSig, int value) public { + require(checkSig(ownerSig, owner)); + require(isPositiveEven(value)); + } + + function isPositiveEven(int value) internal { + require(value > 0); + require(value % 2 == 0); + } +} +``` + +These are general internal functions, not a separate "predicate-only" feature. + +For details on how these internal functions compile to BCH `OP_DEFINE` and `OP_INVOKE`, see [BCH Internal Functions (beta)](/docs/compiler/bch-functions). + +:::caution +Internally-invoked functions currently cannot use `checkSig()`, `checkMultiSig()`, or `checkDataSig()`, and they also cannot reference constructor parameters. Keep signature validation and constructor-parameter-dependent logic in public entrypoint functions for now. +::: + +:::note +When testing contracts that use internal functions locally, configure your `MockNetworkProvider` for `BCH_2026_05` so local evaluation matches the artifact's required VM target. +::: + ### Function Arguments -Function arguments are provided by the user in the unlocking script of the transaction inputs when spending from the contract. Note that function arguments are variables and can be reassigned inside the function body. +Function arguments are provided by the user in the unlocking script of the transaction inputs when spending from the contract. Note that function arguments are variables and can be reassigned inside the function body. User-defined function calls return a boolean value, so they are usually used inside `require(...)` statements. Because the arguments are provided by the user when spending from the contract, these are 'untrusted arguments'. This means that these arguments can be crafted in a specific way by anyone to see if they can exploit the contract logic. @@ -246,12 +292,12 @@ contract P2PKH(bytes20 pkh) { ## Scope -CashScript uses nested scopes for parameters, variables and global functions. There cannot be two identical names within the same scope or within a nested scope. +CashScript uses nested scopes for parameters, variables, user-defined functions and global functions. There cannot be two identical names within the same scope or within a nested scope. There are the following scopes in the nesting order: - **Global scope** - contains global functions and global variables (e.g. `sha256`, `hash160`, `checkSig`, etc.) -- **Contract scope** - contains contract parameters +- **Contract scope** - contains contract parameters and user-defined function names - **Function scope** - contains function parameters and local variables - **Local scope** - contains local variables introduced by control flow blocks (e.g. `if`, `else`) @@ -259,7 +305,7 @@ There are the following scopes in the nesting order: ```solidity // Global scope (contains global functions and global variables like sha256, hash160, checkSig, etc.) -// Contract scope (contains contract parameters - sender, recipient, timeout) +// Contract scope (contains contract parameters and function names) contract TransferWithTimeout( pubkey sender, pubkey recipient, diff --git a/website/docs/language/functions.md b/website/docs/language/functions.md index f407017a..e49fe8b2 100644 --- a/website/docs/language/functions.md +++ b/website/docs/language/functions.md @@ -2,7 +2,7 @@ title: Global Functions --- -CashScript has several built-in functions for things like cryptographic and arithmetic applications, and it includes many common arithmetic and other operators that you would expect in a programming language. +This page documents CashScript's built-in functions. In addition to these built-ins, contract functions can now call other user-defined internal functions in the same contract. For public vs internal function behavior, current visibility rules, restrictions, and how CashScript compiles these calls to BCH `OP_DEFINE` / `OP_INVOKE`, see the [contract structure documentation](/docs/language/contracts#internal-functions-beta) and [BCH Internal Functions (beta)](/docs/compiler/bch-functions). ## Arithmetic functions ### abs() diff --git a/website/docs/sdk/network-provider.md b/website/docs/sdk/network-provider.md index 24062f68..568d1140 100644 --- a/website/docs/sdk/network-provider.md +++ b/website/docs/sdk/network-provider.md @@ -22,6 +22,20 @@ The network parameter can be one of 6 different options. const connectedNetwork = provider.network; ``` +### vmTarget +```ts +provider.vmTarget?: 'BCH_2023_05' | 'BCH_2025_05' | 'BCH_2026_05' | 'BCH_SPEC'; +``` + +Providers can optionally expose the BCH VM target they are configured for. CashScript uses this metadata to validate artifact requirements during local testing, debugging, and transaction-template generation. + +This is especially relevant for contracts using BCH internal-function opcodes like `OP_DEFINE` and `OP_INVOKE`, which currently require `BCH_2026_05`. + +#### Example +```ts +const provider = new MockNetworkProvider({ vmTarget: VmTarget.BCH_2026_05 }); +``` + ### getUtxos() ```ts async provider.getUtxos(address: string): Promise; diff --git a/website/docs/sdk/other-network-providers.md b/website/docs/sdk/other-network-providers.md index f884b9b7..a2b7f8d2 100644 --- a/website/docs/sdk/other-network-providers.md +++ b/website/docs/sdk/other-network-providers.md @@ -39,7 +39,7 @@ interface MockNetworkProvider extends NetworkProvider { The `updateUtxoSet` option is used to determine whether the UTXO set should be updated after a transaction is sent. If `updateUtxoSet` is `true` (default), the UTXO set will be updated to reflect the new state of the mock network. If `updateUtxoSet` is `false`, the UTXO set will not be updated. -The `vmTarget` option defaults to the current VM of `BCH_2025_05`, but this can be changed to test your contract against different BCH virtual machine targets. +The `vmTarget` option defaults to the current VM target used by CashScript, currently `BCH_2026_05`, but this can be changed to test your contract against different BCH virtual machine targets. #### Example ```ts diff --git a/website/docs/sdk/testing-setup.md b/website/docs/sdk/testing-setup.md index 0b3e3c3c..d0a2a39a 100644 --- a/website/docs/sdk/testing-setup.md +++ b/website/docs/sdk/testing-setup.md @@ -17,9 +17,9 @@ To create a new virtual UTXO use `provider.addUtxo(address, utxo)`. You can use #### Example ```ts -import { MockNetworkProvider, randomUtxo, randomToken, randomNFT } from 'cashscript'; +import { MockNetworkProvider, randomUtxo, randomToken, randomNFT, VmTarget } from 'cashscript'; -const provider = new MockNetworkProvider(); +const provider = new MockNetworkProvider({ vmTarget: VmTarget.BCH_2026_05 }); const contractUtxo = provider.addUtxo(contract.address, { vout: 0, txid: "ab...", satoshis: 10000n }); const aliceUtxo = provider.addUtxo(aliceAddress, randomUtxo({ @@ -28,6 +28,10 @@ const aliceUtxo = provider.addUtxo(aliceAddress, randomUtxo({ })); ``` +:::note +If your contract artifact records `compiler.target` metadata, such as contracts using internal functions compiled to BCH `OP_DEFINE` / `OP_INVOKE`, configure your testing provider for the same VM target. Otherwise the SDK will fail fast on target mismatches. +::: + :::note By default, the `MockNetworkProvider` evaluates transactions locally but does not process the transaction updates. This means no UTXOs are consumed and no new UTXOs are created when mocking a transaction `send` using the provider. This can be configured by setting the `updateUtxoSet` option to `true`. :::