Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions examples/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "cashscript-examples",
"private": true,
"version": "0.11.2",
"version": "0.11.3",
"description": "Usage examples of the CashScript SDK",
"main": "p2pkh.js",
"type": "module",
Expand All @@ -13,8 +13,8 @@
"dependencies": {
"@bitauth/libauth": "^3.1.0-next.2",
"@types/node": "^22.10.7",
"cashc": "^0.11.2",
"cashscript": "^0.11.2",
"cashc": "^0.11.3",
"cashscript": "^0.11.3",
"eslint": "^8.56.0",
"typescript": "^5.7.3"
}
Expand Down
6 changes: 3 additions & 3 deletions examples/testing-suite/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "testing-suite",
"version": "0.11.2",
"version": "0.11.3",
"description": "Example project to develop and test CashScript contracts",
"main": "index.js",
"type": "module",
Expand All @@ -26,8 +26,8 @@
},
"dependencies": {
"@bitauth/libauth": "^3.1.0-next.2",
"cashc": "^0.11.2",
"cashscript": "^0.11.2",
"cashc": "^0.11.3",
"cashscript": "^0.11.3",
"url-join": "^5.0.0"
},
"devDependencies": {
Expand Down
4 changes: 2 additions & 2 deletions packages/cashc/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "cashc",
"version": "0.11.2",
"version": "0.11.3",
"description": "Compile Bitcoin Cash contracts to Bitcoin Cash Script or artifacts",
"keywords": [
"bitcoin",
Expand Down Expand Up @@ -52,7 +52,7 @@
},
"dependencies": {
"@bitauth/libauth": "^3.1.0-next.2",
"@cashscript/utils": "^0.11.2",
"@cashscript/utils": "^0.11.3",
"antlr4": "^4.13.2",
"commander": "^13.1.0",
"semver": "^7.6.3"
Expand Down
26 changes: 22 additions & 4 deletions packages/cashc/src/Errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,12 @@ import {
StatementNode,
ContractNode,
ExpressionNode,
SliceNode,
IntLiteralNode,
} from './ast/AST.js';
import { Symbol, SymbolType } from './ast/SymbolTable.js';
import { Location, Point } from './ast/Location.js';
import { BinaryOperator } from './ast/Operator.js';

export class CashScriptError extends Error {
node: Node;
Expand Down Expand Up @@ -73,7 +76,7 @@ export class InvalidSymbolTypeError extends CashScriptError {
}
}

export class RedefinitionError extends CashScriptError {}
export class RedefinitionError extends CashScriptError { }

export class FunctionRedefinitionError extends RedefinitionError {
constructor(
Expand Down Expand Up @@ -160,7 +163,7 @@ export class UnequalTypeError extends TypeError {

export class UnsupportedTypeError extends TypeError {
constructor(
node: BinaryOpNode | UnaryOpNode | TimeOpNode | TupleIndexOpNode,
node: BinaryOpNode | UnaryOpNode | TimeOpNode | TupleIndexOpNode | SliceNode,
actual?: Type,
expected?: Type,
) {
Expand All @@ -170,6 +173,8 @@ export class UnsupportedTypeError extends TypeError {
} else {
super(node, actual, expected, `Tried to call member 'split' on unsupported type '${actual}'`);
}
} else if (node instanceof SliceNode) {
super(node, actual, expected, `Tried to call member 'slice' on unsupported type '${actual}'`);
} else if (node instanceof BinaryOpNode) {
super(node, actual, expected, `Tried to apply operator '${node.operator}' to unsupported type '${actual}'`);
} else if (node instanceof UnaryOpNode && node.operator.startsWith('.')) {
Expand Down Expand Up @@ -249,9 +254,22 @@ export class ArrayElementError extends CashScriptError {

export class IndexOutOfBoundsError extends CashScriptError {
constructor(
node: TupleIndexOpNode,
node: TupleIndexOpNode | BinaryOpNode | SliceNode,
) {
super(node, `Index ${node.index} out of bounds`);
if (node instanceof TupleIndexOpNode) {
super(node, `Index ${node.index} out of bounds`);
} else if (
node instanceof BinaryOpNode && node.operator === BinaryOperator.SPLIT && node.right instanceof IntLiteralNode
) {
const splitIndex = Number(node.right.value);
super(node, `Split index ${splitIndex} out of bounds for type ${node.left.type}`);
} else if (node instanceof SliceNode) {
const start = node.start instanceof IntLiteralNode ? Number(node.start.value) : 'start';
const end = node.end instanceof IntLiteralNode ? Number(node.end.value) : 'end';
super(node, `Slice indexes (${start}, ${end}) out of bounds for type ${node.element.type}`);
} else {
super(node, 'Index out of bounds');
}
}
}

Expand Down
21 changes: 18 additions & 3 deletions packages/cashc/src/ast/AST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ export class ParameterNode extends Node implements Named, Typed {
}
}

export abstract class StatementNode extends Node {}
export abstract class StatementNode extends Node { }

export class VariableDefinitionNode extends StatementNode implements Named, Typed {
constructor(
Expand All @@ -98,8 +98,9 @@ export class VariableDefinitionNode extends StatementNode implements Named, Type

export class TupleAssignmentNode extends StatementNode {
constructor(
public var1: { name: string, type: Type },
public var2: { name: string, type: Type },
// TODO: Use an IdentifierNode instead of a custom type
public left: { name: string, type: Type },
public right: { name: string, type: Type },
public tuple: ExpressionNode,
) {
super();
Expand Down Expand Up @@ -235,6 +236,20 @@ export class TupleIndexOpNode extends ExpressionNode {
}
}

export class SliceNode extends ExpressionNode {
constructor(
public element: ExpressionNode,
public start: ExpressionNode,
public end: ExpressionNode,
) {
super();
}

accept<T>(visitor: AstVisitor<T>): T {
return visitor.visitSlice(this);
}
}

export class BinaryOpNode extends ExpressionNode {
constructor(
public left: ExpressionNode,
Expand Down
11 changes: 11 additions & 0 deletions packages/cashc/src/ast/AstBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import {
NullaryOpNode,
ConsoleStatementNode,
ConsoleParameterNode,
SliceNode,
} from './AST.js';
import { UnaryOperator, BinaryOperator, NullaryOperator } from './Operator.js';
import type {
Expand Down Expand Up @@ -66,6 +67,7 @@ import type {
ConsoleParameterContext,
StatementContext,
RequireMessageContext,
SliceContext,
} from '../grammar/CashScriptParser.js';
import CashScriptVisitor from '../grammar/CashScriptVisitor.js';
import { Location } from './Location.js';
Expand Down Expand Up @@ -262,6 +264,15 @@ export default class AstBuilder
return tupleIndexOp;
}

visitSlice(ctx: SliceContext): SliceNode {
const element = this.visit(ctx._element);
const start = this.visit(ctx._start);
const end = this.visit(ctx._end);
const slice = new SliceNode(element, start, end);
slice.location = Location.fromCtx(ctx);
return slice;
}

visitNullaryOp(ctx: NullaryOpContext): NullaryOpNode {
const operator = ctx.getText() as NullaryOperator;
const nullaryOp = new NullaryOpNode(operator);
Expand Down
8 changes: 8 additions & 0 deletions packages/cashc/src/ast/AstTraversal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
NullaryOpNode,
ConsoleStatementNode,
ConsoleParameterNode,
SliceNode,
} from './AST.js';
import AstVisitor from './AstVisitor.js';

Expand Down Expand Up @@ -113,6 +114,13 @@ export default class AstTraversal extends AstVisitor<Node> {
return node;
}

visitSlice(node: SliceNode): Node {
node.element = this.visit(node.element);
node.start = this.visit(node.start);
node.end = this.visit(node.end);
return node;
}

visitBinaryOp(node: BinaryOpNode): Node {
node.left = this.visit(node.left);
node.right = this.visit(node.right);
Expand Down
2 changes: 2 additions & 0 deletions packages/cashc/src/ast/AstVisitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
TupleAssignmentNode,
NullaryOpNode,
ConsoleStatementNode,
SliceNode,
} from './AST.js';

export default abstract class AstVisitor<T> {
Expand All @@ -42,6 +43,7 @@ export default abstract class AstVisitor<T> {
abstract visitCast(node: CastNode): T;
abstract visitFunctionCall(node: FunctionCallNode): T;
abstract visitInstantiation(node: InstantiationNode): T;
abstract visitSlice(node: SliceNode): T;
abstract visitTupleIndexOp(node: TupleIndexOpNode): T;
abstract visitBinaryOp(node: BinaryOpNode): T;
abstract visitUnaryOp(node: UnaryOpNode): T;
Expand Down
26 changes: 24 additions & 2 deletions packages/cashc/src/generation/GenerateTargetTraversal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import {
NullaryOpNode,
ConsoleParameterNode,
ConsoleStatementNode,
SliceNode,
} from '../ast/AST.js';
import AstTraversal from '../ast/AstTraversal.js';
import { GlobalFunction, Class } from '../ast/Globals.js';
Expand Down Expand Up @@ -251,8 +252,8 @@ export default class GenerateTargetTraversalWithLocation extends AstTraversal {
visitTupleAssignment(node: TupleAssignmentNode): Node {
node.tuple = this.visit(node.tuple);
this.popFromStack(2);
this.pushToStack(node.var1.name);
this.pushToStack(node.var2.name);
this.pushToStack(node.left.name);
this.pushToStack(node.right.name);
return node;
}

Expand Down Expand Up @@ -537,6 +538,27 @@ export default class GenerateTargetTraversalWithLocation extends AstTraversal {
return node;
}

// element.slice(start, end) is equivalent to element.split(end)[0].split(start)[1]
visitSlice(node: SliceNode): Node {
node.element = this.visit(node.element);

const locationData = { location: node.location, positionHint: PositionHint.END };

this.visit(node.end);
this.emit(Op.OP_SPLIT, locationData);
this.emit(Op.OP_DROP, locationData);
this.popFromStack(2);
this.pushToStack('(value)');

this.visit(node.start);
this.emit(Op.OP_SPLIT, locationData);
this.emit(Op.OP_NIP, locationData);
this.popFromStack(2);
this.pushToStack('(value)');

return node;
}

visitBinaryOp(node: BinaryOpNode): Node {
node.left = this.visit(node.left);
node.right = this.visit(node.right);
Expand Down
1 change: 1 addition & 0 deletions packages/cashc/src/grammar/CashScript.g4
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ expression
| scope='tx.inputs' '[' expression ']' op=('.value' | '.lockingBytecode' | '.outpointTransactionHash' | '.outpointIndex' | '.unlockingBytecode' | '.sequenceNumber' | '.tokenCategory' | '.nftCommitment' | '.tokenAmount') # UnaryIntrospectionOp
| expression op=('.reverse()' | '.length') # UnaryOp
| left=expression op='.split' '(' right=expression ')' # BinaryOp
| element=expression '.slice' '(' start=expression ',' end=expression ')' # Slice
| op=('!' | '-') expression # UnaryOp
| left=expression op=('*' | '/' | '%') right=expression # BinaryOp
| left=expression op=('+' | '-') right=expression # BinaryOp
Expand Down
Loading