Python-like Script Language Grammar Specification
This document describes the actual syntax rules implemented in the current project, based on the actual code of the project's lexical analyzer, syntax analyzer, and AST structure.
Complete Grammar Specification (BNF Notation)
// Program structure
program ::= contract
// Contract definition
contract ::= "Contract" IDENTIFIER ":" NEWLINE INDENT [member]* DEDENT
member ::= function | struct | constructor
// Function definition
function ::= "def" IDENTIFIER "(" [parameter_list] ")" ["->" return_type] ":" block
constructor ::= "def" "__init__" "(" [parameter_list] ")" ":" block
parameter_list ::= parameter ["," parameter]*
parameter ::= IDENTIFIER ":" TYPE
// Return type definition
return_type ::= TYPE | brace_type_list
brace_type_list ::= "{" type_list "}"
type_list ::= TYPE ["," TYPE]*
// Struct definition
struct ::= "Struct" IDENTIFIER ":" NEWLINE INDENT [field]+ DEDENT
field ::= IDENTIFIER ":" TYPE NEWLINE
// Statements
statement ::= if_statement
| return_statement
| assignment_statement
| destructure_assignment
| variable_declaration
| expression_statement
block ::= NEWLINE INDENT [statement]* DEDENT
if_statement ::= "if" expression ":" block ["else" ":" block]
return_statement ::= "return" expression NEWLINE
assignment_statement ::= (IDENTIFIER | field_access) "=" expression NEWLINE
destructure_assignment ::= "{" identifier_list "}" "=" expression NEWLINE
identifier_list ::= IDENTIFIER ["," IDENTIFIER]*
variable_declaration ::= IDENTIFIER ":" TYPE ["=" expression] NEWLINE
expression_statement ::= expression NEWLINE
// Expressions
expression ::= binary_expression
| unary_expression
| primary_expression
binary_expression ::= expression binary_operator expression
unary_expression ::= unary_operator expression
primary_expression ::= IDENTIFIER
| literal
| function_call
| method_call
| field_access
| Clone_expression
| brace_expression
| "(" expression ")"
brace_expression ::= "{" [expression_list] "}"
expression_list ::= expression ["," expression]*
function_call ::= IDENTIFIER "(" [argument_list] ")"
method_call ::= expression "." IDENTIFIER "(" [argument_list] ")"
field_access ::= expression "." IDENTIFIER
clone_expression ::= IDENTIFIER "." "Clone" "(" ")"
argument_list ::= expression ["," expression]*
// Literals
literal ::= NUMBER | HEX | STRING
// Operators
binary_operator ::= "+" | "-" | "*" | "/"
| "==" | "!=" | "<" | ">" | "<=" | ">="
unary_operator ::= "-" | "+"
// Lexical elements
IDENTIFIER ::= [a-zA-Z_][a-zA-Z0-9_]*
NUMBER ::= [0-9]+
HEX ::= "0x"[0-9a-fA-F]+
STRING ::= '"' [^"]* '"'
TYPE ::= IDENTIFIER
NEWLINE ::= '\n'
INDENT ::= increase indent level
DEDENT ::= decrease indent level1. Program Structure
1.1 Program Definition
program ::= contractA program consists of a single contract (single contract system).
1.2 Contract Definition
contract ::= "Contract" IDENTIFIER ":" NEWLINE INDENT [member]* DEDENT
member ::= function | struct | constructorContract starts with keyword Contract, followed by contract name, using Python-style indentation structure.
2. Data Structures
2.1 Struct Definition
struct ::= "Struct" IDENTIFIER ":" NEWLINE INDENT [field]+ DEDENT
field ::= IDENTIFIER ":" TYPE NEWLINEStruct definition must contain at least one field, each field must have a type declaration.
Example:
Struct Person:
name: string
age: int
publicKey: hex3. Function Definition
3.1 Ordinary Function
function ::= "def" IDENTIFIER "(" [parameter_list] ")" ["->" TYPE] ":" block
parameter_list ::= parameter ["," parameter]*
parameter ::= IDENTIFIER ":" TYPEFunction parameters must include type declarations, return type is optional.
Example:
def main(sig: string, pubkey: hex) -> bool:
result = CheckSig(pubkey, sig)
return result3.2 Constructor
constructor ::= "def" "__init__" "(" [parameter_list] ")" ":" blockConstructor uses special name __init__.
Example:
def __init__(pubKeyH: hex):
self.pubKeyHash = pubKeyH4. Statements
4.1 Statement Types
statement ::= if_statement
| return_statement
| assignment_statement
| variable_declaration
| expression_statement
block ::= NEWLINE INDENT [statement]* DEDENT4.2 Conditional Statement
if_statement ::= "if" expression ":" block ["else" ":" block]Example:
if check_lock_time(current_time, timeout):
verify checkSig(sender, senderSig)
else:
verify checkSig(recipient, recipientSig)4.3 Return Statement
return_statement ::= "return" expression NEWLINE4.4 Assignment Statement
assignment_statement ::= (IDENTIFIER | field_access) "=" expression NEWLINE
field_access ::= expression "." IDENTIFIERExample:
x = 10
self.value = result
Sub.x = 04.5 Variable Declaration
variable_declaration ::= IDENTIFIER ":" TYPE ["=" expression] NEWLINEExample:
count: int
name: string = "default"
result: bool = CheckSig(pubkey, sig)5. Expressions
5.1 Expression Types
expression ::= binary_expression
| unary_expression
| primary_expression
primary_expression ::= IDENTIFIER
| literal
| function_call
| method_call
| field_access
| "(" expression ")"
literal ::= NUMBER | HEX | STRING5.2 Function Call
function_call ::= IDENTIFIER "(" [argument_list] ")"
argument_list ::= expression ["," expression]*Example:
CheckSig(pubkey, sig)
Sha256(data)
EqualVerify(hash1, hash2)5.3 Method Call and Field Access
method_call ::= expression "." IDENTIFIER "(" [argument_list] ")"
field_access ::= expression "." IDENTIFIERSupports chained calls:
obj.field1.field2
obj.method().field
self.pubKeyHash.a5.4 Binary Operations
binary_expression ::= expression binary_operator expression
binary_operator ::= "+" | "-" | "*" | "/"
| "==" | "!=" | "<" | ">" | "<=" | ">="5.5 Unary Operations
unary_expression ::= unary_operator expression
unary_operator ::= "-" | "+"6. Lexical Elements
6.1 Keywords
keywords ::= "Contract" | "def" | "Struct" | "if" | "else" | "return"6.2 Identifiers and Literals
IDENTIFIER ::= [a-zA-Z_][a-zA-Z0-9_]*
NUMBER ::= [0-9]+
HEX ::= "0x"[0-9a-fA-F]+
STRING ::= '"' [^"]* '"'
TYPE ::= IDENTIFIER // Type identifiers, such as int, string, hex, bool6.3 Operators and Delimiters
operators ::= "=" | "==" | "!=" | "<" | ">" | "<=" | ">="
| "+" | "-" | "*" | "/" | "->"
delimiters ::= "(" | ")" | "[" | "]" | ":" | "," | "."6.4 Indentation and Newlines
The language uses Python-style indentation to represent code block structure:
NEWLINE: newline characterINDENT: increase indent levelDEDENT: decrease indent level
7. Ownership System
The language implements ownership concepts similar to Rust, but only constrains data variables:
7.1 Ownership Rules
- Variable Consumption: When a local variable is used (as function parameter, assigned to other variables, etc.), the ownership of the original variable is transferred
- Contract Member Variables Exception: Contract member variables (like
self.field) are directly replaced in the code during compilation, so they are not subject to ownership constraints and can be used multiple times without Clone - Clone Operation: For local variables, using the
Clone()function can create a copy of the variable, avoiding ownership transfer - Field Access: Accessing fields of a struct instance consumes the ownership of that field, need to clone first if multiple access is needed; but contract member variables are not subject to this restriction
- Special Built-in Functions:
SetAlt()andSetMain()functions do not consume variable ownership
7.2 Clone Syntax
clone_expression ::= IDENTIFIER "." "Clone" "(" ")"Example:
original_value: int = 42
cloned_value: int = original_value.Clone()
# original_value is still available7.3 Built-in Functions
The language provides the following built-in functions:
Clone(): Create a copy of a variable, syntax isvariable.Clone()SetAlt(variable): Move variable to auxiliary stack, does not consume variable ownershipSetMain(variable): Move variable from auxiliary stack to main stack, does not consume variable ownership- Other built-in functions: Such as
CheckSig(),Sha256(), etc., follow ordinary function call syntax and consume parameter ownership
7.4 Ownership System Examples
Ownership transfer of local variables:
data: hex = "0x1234"
result1: bool = CheckSig(data, signature) # data is consumed, cannot be used again
# result2: bool = CheckSig(data, signature2) # Error: data has been consumed
# Need to clone for multiple uses
data2: hex = "0x5678"
cloned_data: hex = data2.Clone()
result1: bool = CheckSig(data2, signature1)
result2: bool = CheckSig(cloned_data, signature2) # Correct: use cloned copySpecial behavior of contract member variables:
def main(sig1: string, sig2: string):
# Contract member variables can be used multiple times, no need for Clone
result1: bool = CheckSig(self.pubKey, sig1)
result2: bool = CheckSig(self.pubKey, sig2) # Correct: self.pubKey can be reused
return result1 and result2Special built-in functions:
data: hex = "0x1234"
SetAlt(data) # data is still available, ownership not consumed
SetMain(data) # data is still available, ownership not consumed8. Brace Syntax Sugar ({} Syntax)
8.1 Syntax Definition
brace_expression ::= "{" [expression_list] "}"
destructure_assignment ::= "{" identifier_list "}" "=" expression NEWLINE8.2 Purpose and Semantics
Brace syntax sugar {} is a multi-functional syntax feature with different semantics based on context:
8.2.1 Multi-return Value Destructuring
Used to receive multiple return values from functions:
{a, b} = Split(data, 10) # Split returns two values, assigned to a and b respectively
{x, y, z} = GetCoordinates() # Receive three return values8.2.2 Struct Literals
Used to create struct instances:
Struct Point:
x: int
y: int
# Use brace syntax sugar to create struct instance
p: Point = {10, 20} # Equivalent to Point(10, 20)8.2.3 Anonymous Structures
When there is no explicit type to receive, {} creates anonymous structures:
# In this case, {a, b} is just syntax sugar, actually a, b are independent variables
{a, b} = Split(data, 10)
# Can directly use a and b, no need to access through struct
result = a + b8.3 Type Inference Rules
The compiler decides the semantics of {} based on the type declaration on the left side:
With explicit struct type: Create instance of that type
pythonpoint: Point = {x, y} # Create Point instanceWithout type declaration: Destructure into independent variables
python{a, b} = Split(data, 10) # a, b are independent variablesMixed usage: Both destructure and create struct
python{x, y}: Point = GetPoint() # Destructure and create Point instance
8.4 Usage Examples
8.4.1 Multi-return Value Functions
def split_data(data: hex, pos: int) -> {hex, hex}:
# Function implementation returns two values
return {left_part, right_part}
# Use destructuring assignment
{left, right} = split_data("0x123456", 3)8.4.2 Struct Construction
Struct Person:
name: string
age: int
# Use brace syntax sugar
person2: Person = {"Bob", 30}8.4.3 Complex Scenarios
Struct Result:
success: bool
data: hex
def process_and_split(input: hex) -> (bool, hex, hex):
# Process and return three values
return success, left, right
# Mixed usage: partial destructuring, partial construction
{success, left, right} = process_and_split(input_data)
result: Result = {success, left} # Use partial results to construct struct9. Syntax Features
- Mandatory Type Declaration: All function parameters, struct fields, and variable declarations must include types
- Python-style Indentation: Use indentation instead of braces to represent code blocks
- Single Contract System: Each file can only contain one contract
- Chained Call Support: Supports chained calls in the form of
obj.field.method() - Constructor: Uses
__init__as constructor name - Field Access: Supports field access in forms like
self.fieldandStructName.field - Ownership System: Variables have consumption property, support clone operations
- Brace Syntax Sugar: Supports multi-return value destructuring and struct literal syntax
10. Type System
Currently supported basic types:
int: integer typestring: string typehex: hexadecimal data typebool: boolean type- User-defined struct types
11. Notes
- All statements must end with newline character
- Code blocks must be properly indented
- Type declarations for function parameters and struct fields are mandatory
- Supports nested field access and method calls
- Assignment operations support simple variables and field access as left values