diff --git a/.github/workflows/check-examples.yml b/.github/workflows/check-examples.yml new file mode 100644 index 0000000..d12f2cb --- /dev/null +++ b/.github/workflows/check-examples.yml @@ -0,0 +1,24 @@ +name: Check Fe Examples + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + check-examples: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Make scripts executable + run: | + chmod +x scripts/fe + chmod +x scripts/extract-fe-blocks.sh + chmod +x scripts/check-examples.sh + + - name: Check Fe code examples + run: npm run check:examples:verbose diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..aee7aef --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,72 @@ +# Contributing to The Fe Guide + +## Code Examples + +The documentation includes Fe code examples that are type-checked to ensure they remain valid as the language evolves. + +### Code Block Conventions + +Use the following markdown annotations for Fe code blocks: + +**Complete examples** (type-checked): +```markdown +```fe +// This code must pass `fe check` +pub contract Example { + // ... +} +```(end) +``` + +**Snippets** (not type-checked): +```markdown +```fe ignore +// This is illustrative, may be incomplete +store.balances[from] -= amount +```(end) +``` + +### Running the Type Checker + +Check all documentation examples: +```bash +npm run check:examples +``` + +Check with verbose output: +```bash +npm run check:examples:verbose +``` + +Check a specific file: +```bash +npm run check:examples -- src/content/docs/examples/erc20.md +``` + +### Adding New Examples + +1. **Complete examples** in `src/content/docs/examples/` should use ` ```fe ` and include all necessary type definitions (see ERC20 example for reference) + +2. **Illustrative snippets** throughout the documentation should use ` ```fe ignore ` since they are not complete programs + +3. Run `npm run check:examples` before committing to verify all examples pass + +### Updating the Fe Binary + +The Fe binary is stored at `bin/fe-linux-x86_64`. To update it: + +1. Build Fe from source or obtain a new binary +2. Copy to `bin/fe-linux-x86_64` +3. Ensure it's executable: `chmod +x bin/fe-linux-x86_64` +4. Run `npm run check:examples` to verify compatibility +5. Commit the updated binary + +Note: Only Linux x86_64 is currently supported for local checking. CI runs on Linux. + +### CI Integration + +The type checker runs automatically on: +- Pull requests to `main` +- Pushes to `main` + +If the check fails, the PR/build will be marked as failed with error details showing the file and line number. diff --git a/bin/fe-linux-x86_64 b/bin/fe-linux-x86_64 new file mode 100755 index 0000000..3a7de97 Binary files /dev/null and b/bin/fe-linux-x86_64 differ diff --git a/openspec/changes/add-fe-type-checking/design.md b/openspec/changes/add-fe-type-checking/design.md new file mode 100644 index 0000000..38e285e --- /dev/null +++ b/openspec/changes/add-fe-type-checking/design.md @@ -0,0 +1,157 @@ +## Context + +The Fe Guide documentation contains numerous code examples that should be valid Fe code. Without automated checking, examples can become stale or contain errors as the language evolves. Since Fe is not yet released, there's no official binary distribution to download. + +**Stakeholders:** +- Documentation authors (need fast feedback) +- Learners (need working examples) +- Fe language maintainers (need to know when language changes break docs) + +**Constraints:** +- Fe has no official releases yet (no downloadable binaries) +- Multi-platform support is desirable but not critical initially +- Some code blocks are intentional snippets, not complete programs + +## Goals / Non-Goals + +**Goals:** +- Catch broken code examples before they reach readers +- Provide fast local feedback for documentation authors +- Automate checking in CI to prevent regressions +- Support both complete examples and intentional snippets + +**Non-Goals:** +- Multi-platform binary support (Linux-first is acceptable) +- Runtime testing of examples (type checking only) +- Automatic fixing of broken examples + +## Decisions + +### Decision: Commit Linux binary to repository +**What:** Store a pre-built `fe` binary for Linux x86_64 directly in the repository. + +**Source:** Copy from local Fe build at `~/Documents/hacking/ef/fe/target/release/fe` + +**Why:** +- Simplest approach given no official Fe releases +- Works immediately for CI (Linux runners) +- Works for Linux developers locally +- No external dependencies or network requirements + +**Alternatives considered:** +- Build from source: Requires Rust toolchain, slower +- Docker: Additional dependency, more complex +- Download from CI artifacts: Requires fe repo changes, fragile + +### Decision: Use `ignore` annotation for snippets +**What:** Code blocks marked ` ```fe ignore ` are skipped during type checking. + +**Why:** +- Simple, explicit opt-out +- Visible in source (documentation intent is clear) +- Compatible with existing markdown tooling +- No complex heuristics needed + +**Alternatives considered:** +- Wrapper templates: More complex, deferred to Phase 3 +- Separate file extension (`.fe-snippet`): Doesn't work in markdown +- Automatic detection: Too fragile, false positives/negatives + +### Decision: Shell script for extraction and checking +**What:** Use bash scripts for the core tooling. + +**Why:** +- No additional dependencies beyond standard Unix tools +- Simple to understand and modify +- Works in CI without setup +- `grep`, `sed`, `awk` are sufficient for markdown parsing + +**Alternatives considered:** +- Node.js script: More dependencies, but better parsing +- Dedicated tool (mdbook-like): Over-engineered for this use case + +### Decision: Error output maps to markdown source +**What:** Errors reference the original markdown file and line, not the extracted temp file. + +**Why:** +- Authors can immediately find and fix issues +- Integrates well with IDE "click to navigate" +- CI annotations can point to correct location + +## Directory Structure + +``` +the-guide/ +├── bin/ +│ └── fe-linux-x86_64 # Pre-built Fe binary +├── scripts/ +│ ├── fe # Wrapper script (platform detection) +│ ├── extract-fe-blocks.sh # Extract code blocks from markdown +│ └── check-examples.sh # Main entry point +├── .github/ +│ └── workflows/ +│ └── check-examples.yml +└── package.json # npm scripts: check:examples +``` + +## Code Block Extraction Algorithm + +``` +1. Find all .md files in src/content/docs/ +2. For each file: + a. Scan for lines matching ```fe (but not ```fe ignore) + b. Record start line number + c. Capture content until closing ``` + d. Write to temp file: {hash}_{file}_{line}.fe +3. Return list of temp files with source mappings +``` + +## Error Mapping Strategy + +When `fe check` reports an error like: +``` +/tmp/examples/abc123_erc20_42.fe:5:10: error: unknown type `u257` +``` + +Transform to: +``` +src/content/docs/examples/erc20.md:46:10: error: unknown type `u257` +``` + +(Line 46 = code block start line 42 + error line 5 - 1) + +## Risks / Trade-offs + +| Risk | Mitigation | +|------|------------| +| Binary becomes stale | Document update process, consider automation | +| Git repo bloat from binary | Use Git LFS if binary > 10MB | +| Non-Linux developers can't check locally | Clear skip message, CI catches issues | +| Snippets incorrectly marked as complete | Phase 2 audit, contributor guidelines | +| Fe language changes break many examples | Run checker before Fe releases, batch fix | + +## Migration Plan + +1. **Phase 1**: Infrastructure + Examples directory only + - Add binary, scripts, CI + - Verify against known-good ERC20 example + - Fix any issues in examples/ + +2. **Phase 2**: Full documentation coverage + - Run against all docs + - Add `ignore` to intentional snippets + - Fix broken complete examples + +3. **Phase 3** (optional): Wrapper templates + - Add support for `wrap=function` etc. + - Convert applicable snippets to checked + +## Resolved Questions + +1. **Binary updates**: Manual process for now. No automation needed initially. + +2. **Git LFS**: Not needed. Binary will be committed directly to the repository. + +3. **Partial checking**: Yes, support checking individual files for faster iteration: + - `npm run check:examples -- path/to/file.md` + - Useful during documentation writing diff --git a/openspec/changes/add-fe-type-checking/proposal.md b/openspec/changes/add-fe-type-checking/proposal.md new file mode 100644 index 0000000..b0413b8 --- /dev/null +++ b/openspec/changes/add-fe-type-checking/proposal.md @@ -0,0 +1,88 @@ +# Change: Add Fe Type Checking for Documentation Examples + +## Why + +Code examples in documentation can drift out of sync with the actual Fe language as it evolves. Currently, there's no automated verification that code examples compile correctly. This leads to: +- Broken examples frustrating learners +- Outdated syntax going unnoticed +- No confidence that documentation reflects working code + +## What Changes + +### 1. Fe Binary Distribution +- Commit a pre-built `fe` binary for Linux x86_64 to the repository +- Store in `bin/fe-linux-x86_64` (or use Git LFS for cleaner history) +- Add wrapper script `scripts/fe` that selects the appropriate binary (future-proofed for multi-platform) + +### 2. Code Block Extraction System +- Script to extract code blocks from markdown files +- Support two types of code blocks: + - ` ```fe ` - Complete, self-contained examples (must compile) + - ` ```fe ignore ` - Snippets for illustration only (skipped) +- Extract to temporary `.fe` files preserving source location for error reporting + +### 3. Type Checking Infrastructure +- `scripts/check-examples.sh` - Main entry point for checking all examples +- Extracts code blocks, runs `fe check`, reports errors with source locations +- Exit code indicates pass/fail for CI integration + +### 4. CI Integration +- GitHub Action runs on PRs and pushes to main +- Fails PR if any ` ```fe ` block doesn't compile +- Clear error messages pointing to the markdown file and line + +### 5. Local Developer Workflow +- `npm run check:examples` - Run type checking locally +- Works on Linux directly, other platforms show helpful skip message +- Fast feedback during documentation writing + +## Code Block Strategy + +### Self-Contained Examples (checked) +```fe +// This compiles as-is +struct Point { + x: u256, + y: u256, +} +``` + +### Snippets (not checked) +```fe ignore +// This is illustrative, missing context +store.balances[from] -= amount +``` + +### Wrapper Templates (future enhancement) +For snippets that should be checked but need context, we could support: +```fe wrap=function +// Wrapped in: fn example() { ... } +let x = 10 +let y = x + 5 +``` + +## Impact + +- Affected specs: New tooling capability +- Affected code: + - New `bin/` directory with fe binary + - New `scripts/` directory with checking scripts + - New GitHub Action workflow + - Markdown files may need `ignore` annotations on snippets +- **BREAKING**: Existing broken examples will be surfaced and need fixing + +## Rollout Strategy + +### Phase 1: Self-Contained Examples Only +- Start with `src/content/docs/examples/` directory +- These are complete, working programs (ERC20, etc.) +- Establishes the infrastructure + +### Phase 2: Expand to All Documentation +- Audit all ` ```fe ` blocks across documentation +- Add `ignore` annotation to intentional snippets +- Fix any broken complete examples + +### Phase 3: Snippet Wrapper Templates (Optional) +- Add wrapper template support for common patterns +- Allows more snippets to be type-checked with minimal boilerplate diff --git a/openspec/changes/add-fe-type-checking/specs/tooling/spec.md b/openspec/changes/add-fe-type-checking/specs/tooling/spec.md new file mode 100644 index 0000000..de4c290 --- /dev/null +++ b/openspec/changes/add-fe-type-checking/specs/tooling/spec.md @@ -0,0 +1,92 @@ +## ADDED Requirements + +### Requirement: Fe Binary Distribution +The documentation repository SHALL include a pre-built Fe compiler binary for type-checking code examples. + +#### Scenario: Binary is available for Linux +- **WHEN** a developer runs the type checker on Linux x86_64 +- **THEN** the pre-built binary SHALL be used without additional setup + +#### Scenario: Binary location is consistent +- **WHEN** the fe binary is needed +- **THEN** it SHALL be located at `bin/fe-linux-x86_64` or accessed via `scripts/fe` + +#### Scenario: Non-Linux platforms receive guidance +- **WHEN** a developer runs the type checker on a non-Linux platform +- **THEN** the system SHALL display a message explaining the limitation and suggesting alternatives + +### Requirement: Code Block Extraction +The system SHALL extract Fe code blocks from markdown documentation files for type checking. + +#### Scenario: Complete examples are extracted +- **WHEN** a markdown file contains a code block marked as ` ```fe ` +- **THEN** the code block contents SHALL be extracted to a temporary `.fe` file + +#### Scenario: Snippets can be excluded +- **WHEN** a markdown file contains a code block marked as ` ```fe ignore ` +- **THEN** the code block SHALL NOT be extracted for type checking + +#### Scenario: Source location is preserved +- **WHEN** a code block is extracted +- **THEN** the original markdown file path and line number SHALL be recorded for error reporting + +### Requirement: Type Checking Execution +The system SHALL run the Fe compiler's type checker on extracted code examples. + +#### Scenario: Successful check +- **WHEN** all extracted code blocks pass type checking +- **THEN** the script SHALL exit with code 0 + +#### Scenario: Failed check +- **WHEN** one or more code blocks fail type checking +- **THEN** the script SHALL exit with a non-zero code +- **AND** SHALL display errors with references to the source markdown file and line + +#### Scenario: Error message clarity +- **WHEN** a type error is found in a code block +- **THEN** the error message SHALL include: + - The markdown file path + - The line number where the code block starts + - The Fe compiler's error message + +### Requirement: Local Developer Workflow +Developers SHALL be able to run type checking locally via npm scripts. + +#### Scenario: npm script availability +- **WHEN** a developer runs `npm run check:examples` +- **THEN** the type checking process SHALL execute + +#### Scenario: Verbose output option +- **WHEN** a developer runs `npm run check:examples:verbose` +- **THEN** additional diagnostic output SHALL be displayed + +### Requirement: CI Integration +The type checker SHALL run automatically in CI on pull requests and main branch pushes. + +#### Scenario: PR validation +- **WHEN** a pull request is opened or updated +- **THEN** the type checker SHALL run as part of CI checks + +#### Scenario: CI failure blocks merge +- **WHEN** type checking fails in CI +- **THEN** the CI check SHALL be marked as failed +- **AND** the PR SHALL be blocked from merging (if branch protection is enabled) + +#### Scenario: Clear CI feedback +- **WHEN** type checking fails in CI +- **THEN** the error output SHALL clearly indicate which documentation file and code block failed + +### Requirement: Code Block Conventions +Documentation authors SHALL follow conventions for marking code blocks. + +#### Scenario: Complete example convention +- **WHEN** a code block contains a complete, compilable Fe program or declaration +- **THEN** it SHALL be marked as ` ```fe ` (no annotation) + +#### Scenario: Snippet convention +- **WHEN** a code block contains an incomplete snippet for illustration +- **THEN** it SHALL be marked as ` ```fe ignore ` + +#### Scenario: Convention documentation +- **WHEN** a contributor writes documentation +- **THEN** the conventions SHALL be documented in a contributor guide diff --git a/openspec/changes/add-fe-type-checking/tasks.md b/openspec/changes/add-fe-type-checking/tasks.md new file mode 100644 index 0000000..ea4e90b --- /dev/null +++ b/openspec/changes/add-fe-type-checking/tasks.md @@ -0,0 +1,56 @@ +# Tasks: Add Fe Type Checking for Documentation Examples + +## 1. Binary Distribution Setup +- [x] 1.1 Create `bin/` directory structure +- [x] 1.2 Copy fe binary from `~/Documents/hacking/ef/fe/target/release/fe` to `bin/fe-linux-x86_64` +- [x] 1.3 Add binary to repository +- [x] 1.4 Create `scripts/fe` wrapper script for platform detection +- [x] 1.5 Document binary update process in CONTRIBUTING.md or similar + +## 2. Code Block Extraction +- [x] 2.1 Create `scripts/extract-fe-blocks.sh` (or Node.js equivalent) +- [x] 2.2 Parse markdown files for ` ```fe ` code blocks +- [x] 2.3 Support `ignore` annotation to skip blocks +- [x] 2.4 Extract blocks to temp directory as `.fe` files +- [x] 2.5 Preserve source file and line number in output filename or metadata +- [x] 2.6 Handle edge cases (nested blocks, frontmatter, etc.) + +## 3. Type Checking Script +- [x] 3.1 Create `scripts/check-examples.sh` main entry point +- [x] 3.2 Call extraction script to get `.fe` files +- [x] 3.3 Run `fe check` on extracted files +- [x] 3.4 Parse fe output and map errors back to markdown source +- [x] 3.5 Format error output for readability (file:line: error) +- [x] 3.6 Return appropriate exit codes (0 = pass, 1 = fail) + +## 4. npm Integration +- [x] 4.1 Add `check:examples` script to package.json +- [x] 4.2 Add `check:examples:verbose` for detailed output +- [x] 4.3 Support partial checking: `npm run check:examples -- path/to/file.md` +- [x] 4.4 Document usage in README or CONTRIBUTING + +## 5. GitHub Action +- [x] 5.1 Create `.github/workflows/check-examples.yml` +- [x] 5.2 Trigger on PR and push to main +- [x] 5.3 Run check script and fail on errors +- [ ] 5.4 Annotate PR with error locations if possible (deferred) +- [ ] 5.5 Cache any dependencies for faster runs (not needed) + +## 6. Documentation Audit (Phase 1 - Examples) +- [x] 6.1 Run checker on `src/content/docs/examples/` +- [x] 6.2 Fix any broken examples found +- [x] 6.3 Verify ERC20 example passes (known good reference) + +## 7. Documentation Audit (Phase 2 - All Docs) +- [x] 7.1 Run checker on all documentation +- [x] 7.2 Identify snippets that need `ignore` annotation +- [x] 7.3 Add `ignore` to intentional snippets +- [x] 7.4 Fix any broken complete examples +- [x] 7.5 Update documentation style guide with code block conventions + +## 8. Testing +- [x] 8.1 Test local workflow on Linux +- [ ] 8.2 Test CI workflow with intentional failure (requires PR) +- [ ] 8.3 Test CI workflow with all passing (requires PR) +- [x] 8.4 Verify helpful error messages for non-Linux platforms +- [x] 8.5 Test with edge cases (empty blocks, special characters, etc.) diff --git a/package.json b/package.json index 92d7f71..a7c6d19 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,9 @@ "start": "astro dev", "build": "astro build", "preview": "astro preview", - "astro": "astro" + "astro": "astro", + "check:examples": "./scripts/check-examples.sh", + "check:examples:verbose": "./scripts/check-examples.sh --verbose" }, "dependencies": { "@astrojs/starlight": "^0.37.1", diff --git a/scripts/add-ignore-to-snippets.sh b/scripts/add-ignore-to-snippets.sh new file mode 100755 index 0000000..f133150 --- /dev/null +++ b/scripts/add-ignore-to-snippets.sh @@ -0,0 +1,26 @@ +#!/bin/bash +# Add 'ignore' annotation to all Fe code blocks that don't already have it +# This marks them as intentional snippets that shouldn't be type-checked + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$SCRIPT_DIR/.." +DOCS_DIR="$PROJECT_ROOT/src/content/docs" + +# Find all markdown files +find "$DOCS_DIR" -name "*.md" | while read -r file; do + # Skip the examples directory - those should be complete examples + if [[ "$file" == *"/examples/"* ]]; then + continue + fi + + # Use sed to replace ```fe (not followed by ignore) with ```fe ignore + # The regex matches ```fe followed by either end of line or whitespace but not 'ignore' + sed -i -E 's/^```fe(\s*)$/```fe ignore\1/g' "$file" + + echo "Processed: $file" +done + +echo "Done! All Fe code blocks (except in examples/) now have 'ignore' annotation." +echo "You can now selectively remove 'ignore' from complete examples that should be checked." diff --git a/scripts/check-examples.sh b/scripts/check-examples.sh new file mode 100755 index 0000000..d688b4a --- /dev/null +++ b/scripts/check-examples.sh @@ -0,0 +1,167 @@ +#!/bin/bash +# Check Fe code examples in documentation for type errors +# Usage: check-examples.sh [--verbose] [file.md ...] + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$SCRIPT_DIR/.." + +# Configuration +VERBOSE=false +FILES=() + +# Parse arguments +while [[ $# -gt 0 ]]; do + case $1 in + -v|--verbose) + VERBOSE=true + shift + ;; + -h|--help) + echo "Usage: check-examples.sh [OPTIONS] [FILE...]" + echo "" + echo "Check Fe code examples in documentation for type errors." + echo "" + echo "Options:" + echo " -v, --verbose Show detailed output including all files checked" + echo " -h, --help Show this help message" + echo "" + echo "Arguments:" + echo " FILE... Specific markdown files to check (default: all docs)" + echo "" + echo "Examples:" + echo " check-examples.sh # Check all documentation" + echo " check-examples.sh src/content/docs/examples/erc20.md" + echo " check-examples.sh --verbose" + exit 0 + ;; + *) + FILES+=("$1") + shift + ;; + esac +done + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[0;33m' +NC='\033[0m' # No Color + +# Create temp directory for extracted blocks +TEMP_DIR=$(mktemp -d) +trap "rm -rf $TEMP_DIR" EXIT + +# Extract Fe code blocks +if [[ "$VERBOSE" == true ]]; then + echo "Extracting Fe code blocks..." +fi + +if [[ ${#FILES[@]} -eq 0 ]]; then + OUTPUT=$("$SCRIPT_DIR/extract-fe-blocks.sh" --output-dir "$TEMP_DIR" 2>&1) +else + OUTPUT=$("$SCRIPT_DIR/extract-fe-blocks.sh" --output-dir "$TEMP_DIR" "${FILES[@]}" 2>&1) +fi + +EXTRACT_DIR=$(echo "$OUTPUT" | head -n1) +EXTRACT_MSG=$(echo "$OUTPUT" | tail -n1) + +if [[ "$VERBOSE" == true ]]; then + echo "$EXTRACT_MSG" +fi + +# Check if any blocks were extracted +MAPPINGS_FILE="$TEMP_DIR/mappings.txt" +if [[ ! -f "$MAPPINGS_FILE" ]] || [[ ! -s "$MAPPINGS_FILE" ]]; then + echo -e "${YELLOW}No Fe code blocks found to check${NC}" + exit 0 +fi + +# Count total blocks +TOTAL_BLOCKS=$(wc -l < "$MAPPINGS_FILE") + +if [[ "$VERBOSE" == true ]]; then + echo "Found $TOTAL_BLOCKS Fe code blocks to check" + echo "" +fi + +# Track errors +ERRORS=() +CHECKED=0 +PASSED=0 +FAILED=0 + +# Check each extracted file +while IFS=: read -r fe_file md_file block_start_line; do + : $((CHECKED++)) + + if [[ "$VERBOSE" == true ]]; then + rel_md="${md_file#$PROJECT_ROOT/}" + echo -n "Checking $rel_md:$block_start_line... " + fi + + # Run fe check on the file + FE_OUTPUT=$("$SCRIPT_DIR/fe" check "$fe_file" 2>&1) || true + + if [[ -z "$FE_OUTPUT" ]]; then + # No output means success + : $((PASSED++)) + if [[ "$VERBOSE" == true ]]; then + echo -e "${GREEN}OK${NC}" + fi + else + # Has output - parse and transform error locations + : $((FAILED++)) + + if [[ "$VERBOSE" == true ]]; then + echo -e "${RED}FAILED${NC}" + fi + + # Get relative path for the markdown file + rel_md="${md_file#$PROJECT_ROOT/}" + + # Transform error output to reference markdown source + # Fe errors typically look like: /path/to/file.fe:LINE:COL: error message + while IFS= read -r error_line; do + if [[ "$error_line" =~ ^[^:]+\.fe:([0-9]+):([0-9]+):(.*)$ ]]; then + fe_line="${BASH_REMATCH[1]}" + fe_col="${BASH_REMATCH[2]}" + error_msg="${BASH_REMATCH[3]}" + + # Calculate markdown line: block_start_line + fe_line + # (block_start_line is the ```fe line, so code starts at +1) + md_line=$((block_start_line + fe_line)) + + ERRORS+=("$rel_md:$md_line:$fe_col:$error_msg") + elif [[ -n "$error_line" ]]; then + # Include other error output as-is but with file context + ERRORS+=("$rel_md:$block_start_line: $error_line") + fi + done <<< "$FE_OUTPUT" + fi +done < "$MAPPINGS_FILE" + +# Print summary +echo "" +echo "======================================" +echo "Fe Code Example Check Results" +echo "======================================" +echo "Total blocks checked: $CHECKED" +echo -e "Passed: ${GREEN}$PASSED${NC}" +echo -e "Failed: ${RED}$FAILED${NC}" +echo "" + +# Print errors if any +if [[ ${#ERRORS[@]} -gt 0 ]]; then + echo -e "${RED}Errors:${NC}" + echo "" + for error in "${ERRORS[@]}"; do + echo " $error" + done + echo "" + exit 1 +fi + +echo -e "${GREEN}All Fe code examples passed type checking!${NC}" +exit 0 diff --git a/scripts/extract-fe-blocks.sh b/scripts/extract-fe-blocks.sh new file mode 100755 index 0000000..7bf378a --- /dev/null +++ b/scripts/extract-fe-blocks.sh @@ -0,0 +1,119 @@ +#!/bin/bash +# Extract Fe code blocks from markdown files for type checking +# Outputs extracted blocks to a temp directory with source mapping + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$SCRIPT_DIR/.." + +# Default docs directory +DOCS_DIR="$PROJECT_ROOT/src/content/docs" + +# Output directory (can be overridden) +OUTPUT_DIR="${FE_EXTRACT_DIR:-$(mktemp -d)}" + +# File to store source mappings +MAPPINGS_FILE="$OUTPUT_DIR/mappings.txt" + +# Parse arguments +FILES=() +while [[ $# -gt 0 ]]; do + case $1 in + --output-dir) + OUTPUT_DIR="$2" + MAPPINGS_FILE="$OUTPUT_DIR/mappings.txt" + shift 2 + ;; + *) + FILES+=("$1") + shift + ;; + esac +done + +# If no files specified, find all markdown files in docs +if [[ ${#FILES[@]} -eq 0 ]]; then + while IFS= read -r -d '' file; do + FILES+=("$file") + done < <(find "$DOCS_DIR" -name "*.md" -o -name "*.mdx" | tr '\n' '\0') +fi + +# Ensure output directory exists +mkdir -p "$OUTPUT_DIR" + +# Clear mappings file +> "$MAPPINGS_FILE" + +# Counter for unique filenames +BLOCK_COUNT=0 + +# Process each markdown file +for md_file in "${FILES[@]}"; do + # Skip if file doesn't exist + [[ -f "$md_file" ]] || continue + + # Get relative path for cleaner output + rel_path="${md_file#$PROJECT_ROOT/}" + + # State machine for parsing + in_fe_block=false + skip_block=false + block_start_line=0 + block_content="" + line_num=0 + + while IFS= read -r line || [[ -n "$line" ]]; do + ((++line_num)) + + if [[ "$in_fe_block" == false ]]; then + # Check for start of Fe code block + if [[ "$line" =~ ^\`\`\`fe ]]; then + # Check if it has 'ignore' annotation + if [[ "$line" =~ ignore ]]; then + skip_block=true + in_fe_block=true + else + skip_block=false + in_fe_block=true + block_start_line=$line_num + block_content="" + fi + fi + else + # Check for end of code block + if [[ "$line" =~ ^\`\`\` ]]; then + in_fe_block=false + + # Only write if not skipped and has content + if [[ "$skip_block" == false && -n "$block_content" ]]; then + ((++BLOCK_COUNT)) + + # Create a safe filename from the source path + safe_name=$(echo "$rel_path" | tr '/' '_' | sed 's/\.md$//' | sed 's/\.mdx$//') + output_file="$OUTPUT_DIR/${BLOCK_COUNT}_${safe_name}_L${block_start_line}.fe" + + # Write the block content + echo "$block_content" > "$output_file" + + # Record the mapping + echo "$output_file:$md_file:$block_start_line" >> "$MAPPINGS_FILE" + fi + + skip_block=false + block_content="" + elif [[ "$skip_block" == false ]]; then + # Accumulate block content + if [[ -n "$block_content" ]]; then + block_content="$block_content"$'\n'"$line" + else + block_content="$line" + fi + fi + fi + done < "$md_file" +done + +# Output results +echo "$OUTPUT_DIR" +echo "Extracted $BLOCK_COUNT Fe code blocks" >&2 diff --git a/scripts/fe b/scripts/fe new file mode 100755 index 0000000..fc3e648 --- /dev/null +++ b/scripts/fe @@ -0,0 +1,37 @@ +#!/bin/bash +# Fe compiler wrapper script with platform detection +# Currently only Linux x86_64 is supported + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +BIN_DIR="$SCRIPT_DIR/../bin" + +# Detect platform +OS="$(uname -s)" +ARCH="$(uname -m)" + +case "$OS-$ARCH" in + Linux-x86_64) + FE_BINARY="$BIN_DIR/fe-linux-x86_64" + ;; + *) + echo "Error: Fe binary not available for $OS-$ARCH" >&2 + echo "" >&2 + echo "Currently only Linux x86_64 is supported." >&2 + echo "Options:" >&2 + echo " 1. Use a Linux machine or VM" >&2 + echo " 2. Use GitHub Codespaces or similar cloud environment" >&2 + echo " 3. Push changes and let CI validate the examples" >&2 + echo "" >&2 + exit 1 + ;; +esac + +if [[ ! -x "$FE_BINARY" ]]; then + echo "Error: Fe binary not found at $FE_BINARY" >&2 + echo "Please ensure the binary exists and is executable." >&2 + exit 1 +fi + +exec "$FE_BINARY" "$@" diff --git a/src/content/docs/appendix/from-rust.md b/src/content/docs/appendix/from-rust.md index 630931a..fab0605 100644 --- a/src/content/docs/appendix/from-rust.md +++ b/src/content/docs/appendix/from-rust.md @@ -11,7 +11,7 @@ Fe draws heavy inspiration from Rust. This guide highlights what's familiar and Fe structs work like Rust structs: -```fe +```fe ignore struct Point { x: u256, y: u256, @@ -25,7 +25,7 @@ let x = p.x Methods are defined in impl blocks: -```fe +```fe ignore struct Counter { value: u256, } @@ -49,7 +49,7 @@ impl Counter { Traits define shared behavior: -```fe +```fe ignore trait Hashable { fn hash(self) -> u256 } @@ -65,7 +65,7 @@ impl Hashable for Point { Type parameters work similarly: -```fe +```fe ignore fn identity(value: T) -> T { value } @@ -85,7 +85,7 @@ impl Wrapper { Constrain generics with trait bounds: -```fe +```fe ignore fn process(item: T) -> u256 { item.hash() } @@ -99,7 +99,7 @@ fn complex(item: T) { Enums with match expressions: -```fe +```fe ignore enum Status { Pending, Active, @@ -119,7 +119,7 @@ fn handle(status: Status) -> u256 { Optional values use `Option`: -```fe +```fe ignore let maybe: Option = Option::Some(42) match maybe { @@ -132,7 +132,7 @@ match maybe { Most constructs are expressions: -```fe +```fe ignore let value = if condition { 10 } else { 20 } let result = match status { @@ -145,7 +145,7 @@ let result = match status { Types are inferred where possible: -```fe +```fe ignore let x = 42 // u256 inferred let y: u8 = 42 // Explicit annotation ``` @@ -154,7 +154,7 @@ let y: u8 = 42 // Explicit annotation Variables are immutable by default: -```fe +```fe ignore let x = 10 // Immutable let mut y = 10 // Mutable y = 20 // OK @@ -166,7 +166,7 @@ y = 20 // OK Fe doesn't have Rust's ownership system. All values are copied or have reference semantics based on context: -```fe +```fe ignore // Rust would require borrowing fn process(data: MyStruct) { // In Fe, no ownership concerns @@ -181,7 +181,7 @@ process(a) // Fine in Fe, would be error in Rust No lifetime annotations needed: -```fe +```fe ignore // Rust: fn longest<'a>(a: &'a str, b: &'a str) -> &'a str // Fe: Just works fn longest(a: String<32>, b: String<32>) -> String<32> { @@ -193,7 +193,7 @@ fn longest(a: String<32>, b: String<32>) -> String<32> { Fe uses an effect system instead of borrowing: -```fe +```fe ignore // Rust: fn modify(data: &mut Storage) // Fe: Effect declaration fn modify() uses mut Storage { @@ -217,7 +217,7 @@ Fe has a standard library, but EVM constraints mean some Rust types aren't avail Fe prefers fixed-size types for EVM efficiency: -```fe +```fe ignore // Rust: String, Vec // Fe: Fixed-size let name: String<32> = "Token" @@ -228,7 +228,7 @@ let data: [u8; 32] = [0; 32] Currently, Fe has basic loop constructs but not yet the full iterator pattern: -```fe +```fe ignore // Rust: items.iter().map(|x| x + 1).collect() // Fe: Currently uses manual loops for i in 0..items.len() { @@ -244,7 +244,7 @@ A trait-based Iterator system similar to Rust's is planned for Fe. This will ena Error handling primarily uses assertions: -```fe +```fe ignore // Rust: Result // Fe: Assertions and revert assert(balance >= amount, "Insufficient balance") @@ -254,7 +254,7 @@ assert(balance >= amount, "Insufficient balance") Fe doesn't support closures: -```fe +```fe ignore // Rust: let add = |a, b| a + b; // Fe: Use named functions fn add(a: u256, b: u256) -> u256 { @@ -285,7 +285,7 @@ Fe organizes code into ingots (packages) containing modules, similar to Rust's c Fe has first-class contract support: -```fe +```fe ignore contract Token { store: TokenStorage, @@ -303,7 +303,7 @@ contract Token { External interfaces are defined separately: -```fe +```fe ignore msg TokenMsg { #[selector = 0xa9059cbb] Transfer { to: Address, amount: u256 } -> bool, @@ -314,7 +314,7 @@ msg TokenMsg { Persistent key-value storage: -```fe +```fe ignore struct Storage { balances: Map, } @@ -324,7 +324,7 @@ struct Storage { Blockchain events for logging: -```fe +```fe ignore struct Transfer { #[indexed] from: Address, @@ -338,7 +338,7 @@ struct Transfer { Explicit capability tracking: -```fe +```fe ignore fn transfer(from: Address, to: Address, amount: u256) uses (mut store: TokenStore, mut log: Log) { diff --git a/src/content/docs/appendix/from-solidity.md b/src/content/docs/appendix/from-solidity.md index b44756a..9ab9292 100644 --- a/src/content/docs/appendix/from-solidity.md +++ b/src/content/docs/appendix/from-solidity.md @@ -24,7 +24,7 @@ contract Token { **Fe**: Functions must declare what state they access via effects. -```fe +```fe ignore fn transfer(from: Address, to: Address, amount: u256) uses mut store: TokenStore // Explicit declaration { @@ -47,7 +47,7 @@ contract Token { **Fe**: External interface is defined separately via messages. -```fe +```fe ignore msg TokenMsg { #[selector = 0xa9059cbb] Transfer { to: Address, amount: u256 } -> bool, @@ -72,7 +72,7 @@ balances[msg.sender] = 100; **Fe**: Access through effect-bound storage. -```fe +```fe ignore // In recv block with (TokenStorage = store) { TokenStorage.balances[caller()] = 100 @@ -113,7 +113,7 @@ function add(uint256 a, uint256 b) public pure returns (uint256) { ``` **Fe**: -```fe +```fe ignore fn add(a: u256, b: u256) -> u256 { a + b } @@ -144,7 +144,7 @@ for (uint i = 0; i < 10; i++) { ``` **Fe**: -```fe +```fe ignore if x > 0 { return true } else { @@ -168,7 +168,7 @@ function transfer(...) { ``` **Fe**: -```fe +```fe ignore struct Transfer { #[indexed] from: Address, @@ -191,7 +191,7 @@ revert("Error message"); ``` **Fe**: -```fe +```fe ignore assert(balance >= amount, "Insufficient balance") revert ``` @@ -206,7 +206,7 @@ constructor(uint256 initialSupply) { ``` **Fe**: -```fe +```fe ignore contract Token { init(initial_supply: u256) uses mut store { store.total_supply = initial_supply @@ -230,7 +230,7 @@ function transfer(address to, uint256 amount) public returns (bool) { ``` **Fe**: -```fe +```fe ignore Transfer { to, amount } -> bool uses (ctx, mut store, mut log) { let from = ctx.caller() assert(store.balances[from] >= amount, "Insufficient balance") @@ -256,7 +256,7 @@ function mint(address to, uint256 amount) public onlyOwner { ``` **Fe**: -```fe +```fe ignore fn require_owner(owner: Address) uses ctx: Ctx { assert(ctx.caller() == owner, "Not owner") } @@ -278,7 +278,7 @@ uint256 allowed = allowances[owner][spender]; ``` **Fe**: -```fe +```fe ignore struct Storage { allowances: Map<(Address, Address), u256>, } @@ -293,7 +293,7 @@ let allowed = store.allowances[(owner, spender)] Fe doesn't have contract inheritance. Use composition instead: -```fe +```fe ignore // Instead of: contract Token is Ownable, Pausable contract Token { auth: AccessControl, // Composition @@ -305,7 +305,7 @@ contract Token { Fe doesn't have function modifiers. Use helper functions: -```fe +```fe ignore fn require_not_paused(paused: bool) { assert(!paused, "Contract is paused") } @@ -318,7 +318,7 @@ require_not_paused(store.paused) Fe requires explicit ABI selectors: -```fe +```fe ignore msg TokenMsg { #[selector = 0xa9059cbb] // Must specify Transfer { to: Address, amount: u256 } -> bool, @@ -329,7 +329,7 @@ msg TokenMsg { Fe doesn't support function overloading. Use different names: -```fe +```fe ignore // Instead of transfer(address) and transfer(address, uint256) fn transfer_to(to: Address) { ... } fn transfer_amount(to: Address, amount: u256) { ... } @@ -339,7 +339,7 @@ fn transfer_amount(to: Address, amount: u256) { ... } Fe infers types where possible: -```fe +```fe ignore let x = 10 // u256 inferred let y: u8 = 10 // Explicit when needed ``` diff --git a/src/content/docs/appendix/intrinsics.md b/src/content/docs/appendix/intrinsics.md index 4f6eade..2f8f82e 100644 --- a/src/content/docs/appendix/intrinsics.md +++ b/src/content/docs/appendix/intrinsics.md @@ -24,7 +24,7 @@ These intrinsics provide information about the execution context: ### Usage -```fe +```fe ignore fn only_owner(owner: Address) uses ctx: Ctx { assert(ctx.caller() == owner, "not owner") } @@ -48,7 +48,7 @@ These intrinsics relate to contract state and identity: ### Usage -```fe +```fe ignore fn get_contract_balance() -> u256 { self_balance() } @@ -71,7 +71,7 @@ Hash functions and cryptographic operations: ### Usage -```fe +```fe ignore fn hash_value(value: u256) -> u256 { keccak256(value) } @@ -100,7 +100,7 @@ Control flow for error handling: ### Usage -```fe +```fe ignore fn transfer(from: Address, to: Address, amount: u256) { assert(from != Address::zero(), "transfer from zero address") assert(to != Address::zero(), "transfer to zero address") @@ -128,7 +128,7 @@ For calling other contracts: ### Usage -```fe +```fe ignore fn send_eth(to: Address, amount: u256) -> bool { let (success, _) = call(to, amount, []) success @@ -172,7 +172,7 @@ Event emission: ### Usage -```fe +```fe ignore struct TransferEvent { #[indexed] from: Address, @@ -194,7 +194,7 @@ fn emit_transfer(from: Address, to: Address, value: u256) uses mut log: Log { ### Usage -```fe +```fe ignore fn deposit() uses ctx: Ctx { let amount = ctx.msg_value() // Process deposit... @@ -209,7 +209,7 @@ fn deposit() uses ctx: Ctx { ### Usage -```fe +```fe ignore fn get_recent_block_hash(block_num: u256) -> u256 { block_hash(block_num) } diff --git a/src/content/docs/appendix/keywords.md b/src/content/docs/appendix/keywords.md index 4f28b5f..f8d34c3 100644 --- a/src/content/docs/appendix/keywords.md +++ b/src/content/docs/appendix/keywords.md @@ -81,7 +81,7 @@ These identifiers have special meaning in certain contexts: ## Quick Reference by Category ### Declarations -```fe +```fe ignore const MAX_SUPPLY: u256 = 1000000 let balance = 0 let mut counter = 0 @@ -95,7 +95,7 @@ msg TokenMsg { } ``` ### Control Flow -```fe +```fe ignore if condition { } else { } match value { } for item in items { } @@ -105,14 +105,14 @@ return value ``` ### Effects -```fe +```fe ignore fn foo() uses Storage { } fn foo() uses mut Storage { } with (Storage = store) { } ``` ### Contracts -```fe +```fe ignore contract Token { init() { } recv Msg { } diff --git a/src/content/docs/appendix/selectors.md b/src/content/docs/appendix/selectors.md index eb95ccf..0930a6d 100644 --- a/src/content/docs/appendix/selectors.md +++ b/src/content/docs/appendix/selectors.md @@ -88,7 +88,7 @@ When calculating selectors, use these canonical type names: In Fe, you specify selectors explicitly in message definitions: -```fe +```fe ignore msg Erc20 { #[selector = 0xa9059cbb] Transfer { to: Address, amount: u256 } -> bool, @@ -149,7 +149,7 @@ Many online tools can compute selectors: :::note[Planned Feature] Fe will provide a `sol_sig` const function for computing selectors at compile time: -```fe +```fe ignore msg TokenMsg { #[selector = sol_sig("balanceOf(address)")] BalanceOf { account: Address } -> u256, diff --git a/src/content/docs/appendix/types.md b/src/content/docs/appendix/types.md index 359b9ae..aa100e4 100644 --- a/src/content/docs/appendix/types.md +++ b/src/content/docs/appendix/types.md @@ -20,7 +20,7 @@ Unsigned integers cannot be negative. Fe provides sizes matching EVM word bounda ### Usage -```fe +```fe ignore let small: u8 = 255 let balance: u256 = 1000000000000000000 // 1 ETH in wei let max_supply: u256 = 21000000 @@ -47,7 +47,7 @@ Signed integers can be negative. They use two's complement representation: ### Usage -```fe +```fe ignore let delta: i256 = -100 let temperature: i32 = -40 let positive: i256 = 1000 @@ -69,7 +69,7 @@ The `bool` type represents true/false values: ### Usage -```fe +```fe ignore let is_active: bool = true let paused: bool = false @@ -97,7 +97,7 @@ Fixed-size strings with a maximum length: ### Usage -```fe +```fe ignore let name: String<32> = "CoolCoin" let symbol: String<8> = "COOL" let message: String<64> = "Transfer failed" @@ -119,7 +119,7 @@ Fixed-size collections of heterogeneous types: ### Usage -```fe +```fe ignore let pair: (u256, bool) = (100, true) let triple: (u256, u256, u256) = (1, 2, 3) @@ -141,7 +141,7 @@ Fixed-size collections of homogeneous types: ### Usage -```fe +```fe ignore let numbers: [u256; 3] = [1, 2, 3] let first = numbers[0] ``` @@ -156,7 +156,7 @@ The empty tuple, representing no value: ### Usage -```fe +```fe ignore fn do_something() { // Implicitly returns () } @@ -176,7 +176,7 @@ Represents an optional value: ### Usage -```fe +```fe ignore let maybe_value: Option = Option::Some(42) let nothing: Option = Option::None @@ -196,7 +196,7 @@ Key-value storage mapping: ### Usage -```fe +```fe ignore struct Storage { balances: Map, allowances: Map<(Address, Address), u256>, @@ -219,7 +219,7 @@ storage.balances[account] = new_balance Convert between numeric types with `as`: -```fe +```fe ignore let small: u8 = 100 let big: u256 = small as u256 diff --git a/src/content/docs/compound-types/enums.md b/src/content/docs/compound-types/enums.md index e553cbd..fe4f395 100644 --- a/src/content/docs/compound-types/enums.md +++ b/src/content/docs/compound-types/enums.md @@ -9,7 +9,7 @@ Enums (enumerations) define a type that can be one of several distinct variants. Define an enum with the `enum` keyword: -```fe +```fe ignore enum Direction { North South @@ -20,7 +20,7 @@ enum Direction { Use `pub` for public visibility: -```fe +```fe ignore pub enum Status { Active Inactive @@ -36,7 +36,7 @@ Fe supports three kinds of enum variants: Variants with no associated data: -```fe +```fe ignore enum Color { Red Green @@ -50,7 +50,7 @@ let color = Color::Red Variants with unnamed fields: -```fe +```fe ignore enum Message { Quit Move(i32, i32) @@ -67,7 +67,7 @@ Access tuple variant data through pattern matching. Variants with named fields: -```fe +```fe ignore enum Event { Click { x: i32, y: i32 } KeyPress { code: u32, shift: bool } @@ -83,7 +83,7 @@ let event = Event::Click { x: 100, y: 200 } Use the `EnumName::VariantName` syntax: -```fe +```fe ignore enum Option { Some(T) None @@ -97,7 +97,7 @@ let absent = Option::None Match expressions extract data from enum variants: -```fe +```fe ignore enum Result { Ok(T) Err(E) @@ -119,7 +119,7 @@ fn handle_result(result: Result) { Match expressions must handle all variants. The compiler enforces this: -```fe +```fe ignore enum Status { Active Inactive @@ -135,7 +135,7 @@ match status { Use the wildcard `_` to match remaining variants: -```fe +```fe ignore match status { Status::Active => "running" _ => "not running" // Matches Inactive and Pending @@ -148,7 +148,7 @@ match status { Bind variant data to variables: -```fe +```fe ignore enum Message { Text(String) Number(u256) @@ -168,7 +168,7 @@ match message { Match on struct variant fields: -```fe +```fe ignore enum Event { Click { x: i32, y: i32 } } @@ -182,7 +182,7 @@ match event { Use `..` to ignore some fields: -```fe +```fe ignore match event { Event::Click { x, .. } => { // Only use x, ignore y @@ -194,7 +194,7 @@ match event { Match nested structures: -```fe +```fe ignore enum Outer { Inner(Option) } @@ -213,7 +213,7 @@ match outer { Enums can have type parameters: -```fe +```fe ignore enum Option { Some(T) None @@ -227,7 +227,7 @@ enum Result { ### Using Generic Enums -```fe +```fe ignore fn find_user(id: u256) -> Option { if id == 0 { return Option::None @@ -247,7 +247,7 @@ fn divide(a: u256, b: u256) -> Result { Constrain generic types: -```fe +```fe ignore enum Container { Single(T) Pair(T, T) @@ -258,7 +258,7 @@ enum Container { Match is an expression that returns a value: -```fe +```fe ignore let description = match status { Status::Active => "System is running" Status::Inactive => "System is stopped" @@ -274,7 +274,7 @@ All match arms must return the same type. Represent optional values: -```fe +```fe ignore enum Option { Some(T) None @@ -293,7 +293,7 @@ fn get_balance(account_id: u256) -> Option { Represent success or failure: -```fe +```fe ignore enum Result { Ok(T) Err(E) diff --git a/src/content/docs/compound-types/maps.md b/src/content/docs/compound-types/maps.md index 1616dd6..54547cf 100644 --- a/src/content/docs/compound-types/maps.md +++ b/src/content/docs/compound-types/maps.md @@ -13,7 +13,7 @@ The current `StorageMap` is a minimal implementation that will be replaced with `StorageMap` stores key-value pairs in contract storage: -```fe +```fe ignore use std::storage::StorageMap pub struct MyContract { @@ -28,7 +28,7 @@ Unlike in-memory data structures, storage maps persist on the blockchain between Declare maps as fields in contract structs: -```fe +```fe ignore pub struct Token { balances: StorageMap, total_supply: u256, @@ -43,7 +43,7 @@ Maps are always stored in contract storage—they cannot be created as local var Use `get(key)` to read a value: -```fe +```fe ignore impl Token { pub fn balance_of(self, account_id: u256) -> u256 { self.balances.get(account_id) @@ -57,7 +57,7 @@ If a key hasn't been set, `get` returns the default value for the value type (ty Use `set(key, value)` to store a value: -```fe +```fe ignore impl Token { pub fn set_balance(mut self, account_id: u256, amount: u256) { self.balances.set(account_id, amount) @@ -71,7 +71,7 @@ Note that the `self` parameter must be `mut` to modify storage. Use tuples as keys for multi-dimensional mappings: -```fe +```fe ignore pub struct Token { // Maps (owner_id, spender_id) to allowance amount allowances: StorageMap<(u256, u256), u256>, @@ -121,7 +121,7 @@ This ensures: ### Token Balances -```fe +```fe ignore pub struct Token { balances: StorageMap, } @@ -139,7 +139,7 @@ impl Token { ### Allowance System -```fe +```fe ignore pub struct Token { balances: StorageMap, allowances: StorageMap<(u256, u256), u256>, @@ -172,7 +172,7 @@ impl Token { ### Role-Based Access -```fe +```fe ignore pub struct AccessControl { // Maps (account_id, role_id) to whether role is granted roles: StorageMap<(u256, u256), bool>, diff --git a/src/content/docs/compound-types/structs.md b/src/content/docs/compound-types/structs.md index 113ef11..8ddedb3 100644 --- a/src/content/docs/compound-types/structs.md +++ b/src/content/docs/compound-types/structs.md @@ -9,7 +9,7 @@ Structs are custom data types that group related values under named fields. They Define a struct with the `struct` keyword: -```fe +```fe ignore struct Point { x: i32, y: i32, @@ -22,7 +22,7 @@ Each field has a name and a type. Fields are separated by commas. By default, struct fields are private. Use `pub` to make them publicly accessible: -```fe +```fe ignore pub struct User { pub name: String, pub balance: u256, @@ -32,7 +32,7 @@ pub struct User { The struct itself can also be public or private: -```fe +```fe ignore pub struct PublicStruct { pub field: u256, } @@ -46,7 +46,7 @@ struct PrivateStruct { Create a struct instance by specifying values for all fields: -```fe +```fe ignore struct Point { x: i32, y: i32, @@ -62,7 +62,7 @@ All fields must be initialized—there are no default values. When a variable has the same name as a field, use shorthand syntax: -```fe +```fe ignore let x = 10 let y = 20 @@ -73,7 +73,7 @@ let point = Point { x, y } // Same as Point { x: x, y: y } Access struct fields with dot notation: -```fe +```fe ignore let point = Point { x: 10, y: 20 } let x_value = point.x // 10 @@ -84,7 +84,7 @@ let y_value = point.y // 20 For mutable struct instances, update fields directly: -```fe +```fe ignore let mut point = Point { x: 0, y: 0 } point.x = 10 @@ -95,7 +95,7 @@ point.y = 20 Structs can have generic type parameters: -```fe +```fe ignore struct Pair { first: T, second: T, @@ -109,7 +109,7 @@ let bool_pair = Pair { first: true, second: false } Use multiple generic parameters for different field types: -```fe +```fe ignore struct KeyValue { key: K, value: V, @@ -122,7 +122,7 @@ let entry = KeyValue { key: "name", value: 42 } Constrain generic types with trait bounds: -```fe +```fe ignore struct Container { item: T, } @@ -132,7 +132,7 @@ struct Container { Structs can contain other structs: -```fe +```fe ignore struct Point { x: i32, y: i32, @@ -155,7 +155,7 @@ let x = rect.top_left.x // 0 Destructure structs in patterns: -```fe +```fe ignore let point = Point { x: 10, y: 20 } let Point { x, y } = point @@ -164,7 +164,7 @@ let Point { x, y } = point Use `..` to ignore remaining fields: -```fe +```fe ignore struct User { name: String, balance: u256, @@ -178,7 +178,7 @@ let User { name, .. } = user // Only extract name Match on struct patterns: -```fe +```fe ignore match point { Point { x: 0, y: 0 } => "origin" Point { x: 0, y } => "on y-axis" @@ -191,7 +191,7 @@ match point { Combine structs and tuples: -```fe +```fe ignore struct Line { start: (i32, i32), end: (i32, i32), diff --git a/src/content/docs/compound-types/tuples.md b/src/content/docs/compound-types/tuples.md index 1db447e..c7957cd 100644 --- a/src/content/docs/compound-types/tuples.md +++ b/src/content/docs/compound-types/tuples.md @@ -9,7 +9,7 @@ Tuples group multiple values of different types into a single compound value. Th Declare a tuple type by listing element types in parentheses: -```fe +```fe ignore let point: (i32, i32) = (10, 20) let mixed: (u256, bool, String) = (100, true, "hello") ``` @@ -18,7 +18,7 @@ let mixed: (u256, bool, String) = (100, true, "hello") Create tuples by listing values in parentheses: -```fe +```fe ignore let coordinates = (100, 200) let user_data = (address, balance, is_active) ``` @@ -27,7 +27,7 @@ let user_data = (address, balance, is_active) A single-element tuple requires a trailing comma to distinguish it from a parenthesized expression: -```fe +```fe ignore let single: (u256,) = (42,) // This is a tuple let not_tuple: u256 = (42) // This is just 42 ``` @@ -36,7 +36,7 @@ let not_tuple: u256 = (42) // This is just 42 The empty tuple `()` is called the "unit" type. It represents the absence of a value: -```fe +```fe ignore let nothing: () = () ``` @@ -46,7 +46,7 @@ Functions that don't return a value implicitly return `()`. Access tuple elements by index using dot notation: -```fe +```fe ignore let point = (10, 20, 30) let x = point.0 // 10 @@ -60,7 +60,7 @@ Indices start at 0 and must be literal integers—you cannot use a variable as a Extract all tuple elements at once with pattern matching: -```fe +```fe ignore let point = (100, 200) let (x, y) = point @@ -69,7 +69,7 @@ let (x, y) = point Use `_` to ignore elements you don't need: -```fe +```fe ignore let data = (address, amount, timestamp) let (_, amount, _) = data // Only extract amount ``` @@ -78,7 +78,7 @@ let (_, amount, _) = data // Only extract amount Destructure tuples directly in function signatures: -```fe +```fe ignore fn process_point((x, y): (i32, i32)) -> i32 { x + y } @@ -90,7 +90,7 @@ fn process_point((x, y): (i32, i32)) -> i32 { Tuples are commonly used to return multiple values from a function: -```fe +```fe ignore fn get_bounds() -> (u256, u256) { (0, 1000) } @@ -102,7 +102,7 @@ let (min, max) = get_bounds() Pass tuples as function arguments: -```fe +```fe ignore fn calculate_distance(start: (i32, i32), end: (i32, i32)) -> i32 { let (x1, y1) = start let (x2, y2) = end @@ -114,7 +114,7 @@ fn calculate_distance(start: (i32, i32), end: (i32, i32)) -> i32 { Tuples can contain other tuples: -```fe +```fe ignore let nested: ((i32, i32), (i32, i32)) = ((0, 0), (100, 100)) let start = nested.0 // (0, 0) @@ -125,7 +125,7 @@ let start_x = nested.0.0 // 0 Match on tuple patterns: -```fe +```fe ignore let point = (0, 5) match point { diff --git a/src/content/docs/contracts/composition.md b/src/content/docs/contracts/composition.md index d76ce7b..5a8424d 100644 --- a/src/content/docs/contracts/composition.md +++ b/src/content/docs/contracts/composition.md @@ -12,7 +12,7 @@ Unlike Solidity's inheritance, Fe uses composition through: - **Modular storage structs** for logical separation - **Multiple recv blocks** for interface organization -```fe +```fe ignore // Modular storage pub struct BalanceStorage { /* ... */ } pub struct OwnerStorage { /* ... */ } @@ -33,7 +33,7 @@ contract Token { Extract business logic into functions that declare their effect dependencies: -```fe +```fe ignore pub struct TokenStorage { pub balances: StorageMap, pub total_supply: u256, @@ -73,7 +73,7 @@ fn transfer(from: u256, to: u256, amount: u256) uses mut TokenStorage -> bool { Split storage into logical units: -```fe +```fe ignore // Core token state pub struct BalanceStorage { pub balances: StorageMap, @@ -126,7 +126,7 @@ contract Token { Implement access control as a reusable module: -```fe +```fe ignore pub struct OwnerStorage { pub owner: u256, } @@ -174,7 +174,7 @@ contract OwnableToken { ## Pausable Pattern -```fe +```fe ignore pub struct PauseStorage { pub paused: bool, } @@ -229,7 +229,7 @@ contract PausableToken { Functions can require multiple effects: -```fe +```fe ignore fn guarded_transfer( from: u256, to: u256, diff --git a/src/content/docs/contracts/declaration.md b/src/content/docs/contracts/declaration.md index f8c1dce..2f78133 100644 --- a/src/content/docs/contracts/declaration.md +++ b/src/content/docs/contracts/declaration.md @@ -9,7 +9,7 @@ Contracts are the primary building blocks of Fe smart contract development. They Declare a contract with the `contract` keyword: -```fe +```fe ignore contract Token { // Contract body } @@ -20,7 +20,7 @@ A contract can contain: - **Init block**: Constructor logic (optional) - **Recv blocks**: Message handlers -```fe +```fe ignore contract Token { // Fields store: TokenStorage, @@ -41,7 +41,7 @@ contract Token { Fields declare the contract's storage and effect dependencies: -```fe +```fe ignore pub struct TokenStorage { pub balances: StorageMap, pub total_supply: u256, @@ -62,7 +62,7 @@ Unlike structs, contracts have specific restrictions: - **No associated functions**: Contracts don't have `fn` declarations inside them - **No direct field access**: Storage is accessed through effects, not `self.field` -```fe +```fe ignore // WRONG: Contracts cannot have impl blocks contract Token { store: TokenStorage, @@ -75,7 +75,7 @@ impl Token { // Error! Instead, use standalone functions with effects: -```fe +```fe ignore // CORRECT: Use functions with effects fn get_balance(account: u256) uses TokenStorage -> u256 { TokenStorage.balances.get(account) @@ -98,7 +98,7 @@ contract Token { The canonical structure of a Fe contract: -```fe +```fe ignore // 1. Storage struct definition pub struct TokenStorage { pub balances: StorageMap, @@ -155,7 +155,7 @@ contract Token { A single Fe file can define multiple contracts: -```fe +```fe ignore contract TokenA { store: TokenStorage, // ... diff --git a/src/content/docs/contracts/effects.md b/src/content/docs/contracts/effects.md index 5570fe0..e1e62f3 100644 --- a/src/content/docs/contracts/effects.md +++ b/src/content/docs/contracts/effects.md @@ -9,7 +9,7 @@ Contracts bridge the gap between storage and the effect system. Contract fields When you declare a contract field with a storage type, that field can be provided as an effect: -```fe +```fe ignore pub struct TokenStorage { pub balances: StorageMap, pub total_supply: u256, @@ -38,7 +38,7 @@ The `with (TokenStorage = store)` expression: The primary use of contract-level effects is providing them to helper functions: -```fe +```fe ignore fn get_balance(account: u256) uses TokenStorage -> u256 { TokenStorage.balances.get(account) } @@ -72,7 +72,7 @@ contract Token { Use `mut` when you need to modify storage: -```fe +```fe ignore contract Token { store: TokenStorage, @@ -101,7 +101,7 @@ When calling functions that require `mut` effects, the `with` block automaticall Contracts can have multiple fields for different effects: -```fe +```fe ignore pub struct TokenStorage { pub balances: StorageMap, pub total_supply: u256, @@ -142,7 +142,7 @@ contract Token { Effects are only available within their `with` block: -```fe +```fe ignore recv TokenMsg { Transfer { to, amount } -> bool { with (TokenStorage = store) { @@ -180,7 +180,7 @@ contract Token { } ``` -```fe +```fe ignore // Fe - explicit effect dependency fn transfer(to: u256, amount: u256) uses mut TokenStorage -> bool { // Clear that this function needs TokenStorage diff --git a/src/content/docs/contracts/init.md b/src/content/docs/contracts/init.md index c3f1be7..790e7ac 100644 --- a/src/content/docs/contracts/init.md +++ b/src/content/docs/contracts/init.md @@ -9,7 +9,7 @@ The `init` block is Fe's constructor—it runs once when the contract is deploye Declare an init block inside a contract: -```fe +```fe ignore contract Token { store: TokenStorage, @@ -25,7 +25,7 @@ The init block runs automatically during deployment. Init blocks can accept parameters passed during deployment: -```fe +```fe ignore contract Token { store: TokenStorage, @@ -42,7 +42,7 @@ These parameters are encoded in the deployment transaction's calldata. The primary purpose of init is setting up storage: -```fe +```fe ignore pub struct TokenStorage { pub balances: StorageMap, pub total_supply: u256, @@ -67,7 +67,7 @@ contract Token { Unlike recv handlers, the init block can access storage fields directly: -```fe +```fe ignore init(supply: u256) { store.total_supply = supply // Direct access allowed in init } @@ -79,7 +79,7 @@ This is because init runs in a special context during deployment, before normal Contracts can have parameter-less init blocks: -```fe +```fe ignore contract Counter { store: CounterStorage, @@ -93,7 +93,7 @@ contract Counter { The init block is optional. Contracts without init have default-initialized storage (zeros): -```fe +```fe ignore contract Simple { store: SimpleStorage, // No init block - storage starts at default values @@ -108,7 +108,7 @@ contract Simple { Init can perform multiple setup operations: -```fe +```fe ignore pub struct TokenStorage { pub balances: StorageMap, pub allowances: StorageMap>, @@ -148,7 +148,7 @@ The init block has some restrictions: While init can access storage directly, it doesn't use the effect system in the same way as handlers: -```fe +```fe ignore // In init: direct access init(supply: u256) { store.total_supply = supply diff --git a/src/content/docs/contracts/receive-blocks.md b/src/content/docs/contracts/receive-blocks.md index f495604..65b551a 100644 --- a/src/content/docs/contracts/receive-blocks.md +++ b/src/content/docs/contracts/receive-blocks.md @@ -9,7 +9,7 @@ Receive blocks inside contracts handle incoming messages and have access to the In a contract, recv blocks access storage via `with` expressions: -```fe +```fe ignore pub struct TokenStorage { pub balances: StorageMap, pub total_supply: u256, @@ -46,7 +46,7 @@ contract Token { The `with` expression binds a contract field to an effect type: -```fe +```fe ignore with (TokenStorage = store) { // TokenStorage effect is available here TokenStorage.balances.get(account) @@ -62,7 +62,7 @@ This pattern: Handlers typically delegate to helper functions: -```fe +```fe ignore fn get_balance(account: u256) uses TokenStorage -> u256 { TokenStorage.balances.get(account) } @@ -103,7 +103,7 @@ contract Token { When handlers need multiple storage types: -```fe +```fe ignore pub struct BalanceStorage { pub balances: StorageMap, } @@ -130,7 +130,7 @@ contract Token { A full ERC20-style token contract: -```fe +```fe ignore pub struct TokenStorage { pub balances: StorageMap, pub allowances: StorageMap>, @@ -244,7 +244,7 @@ contract Token { For contracts with many handlers, organize by interface: -```fe +```fe ignore contract Token { store: TokenStorage, diff --git a/src/content/docs/contracts/storage.md b/src/content/docs/contracts/storage.md index 622e477..802e8cb 100644 --- a/src/content/docs/contracts/storage.md +++ b/src/content/docs/contracts/storage.md @@ -9,7 +9,7 @@ Storage fields hold the persistent state of a contract. In Fe, storage is define Storage is defined as a struct with storage-compatible fields: -```fe +```fe ignore pub struct TokenStorage { pub balances: StorageMap, pub total_supply: u256, @@ -28,7 +28,7 @@ The contract field `store` holds an instance of `TokenStorage`, which persists b All primitive types can be stored: -```fe +```fe ignore pub struct Config { pub enabled: bool, pub count: u256, @@ -40,7 +40,7 @@ pub struct Config { For key-value mappings, use `StorageMap`: -```fe +```fe ignore pub struct TokenStorage { // Maps account -> balance pub balances: StorageMap, @@ -58,7 +58,7 @@ The current `StorageMap` is a temporary implementation that will be replaced wit Storage structs can contain other structs: -```fe +```fe ignore pub struct Metadata { pub name_length: u256, pub decimals: u8, @@ -74,7 +74,7 @@ pub struct TokenStorage { Storage is accessed through effects, not directly: -```fe +```fe ignore fn get_balance(account: u256) uses TokenStorage -> u256 { TokenStorage.balances.get(account) } @@ -86,7 +86,7 @@ fn set_balance(account: u256, amount: u256) uses mut TokenStorage { In handlers, provide the contract field as an effect: -```fe +```fe ignore contract Token { store: TokenStorage, @@ -106,7 +106,7 @@ contract Token { Retrieve a value (returns zero/default if not set): -```fe +```fe ignore let balance = TokenStorage.balances.get(account) ``` @@ -114,7 +114,7 @@ let balance = TokenStorage.balances.get(account) Store a value: -```fe +```fe ignore TokenStorage.balances.set(account, new_balance) ``` @@ -122,7 +122,7 @@ TokenStorage.balances.set(account, new_balance) For nested mappings, chain the operations: -```fe +```fe ignore pub struct AllowanceStorage { // owner -> spender -> amount pub allowances: StorageMap>, @@ -141,7 +141,7 @@ fn set_allowance(owner: u256, spender: u256, amount: u256) uses mut AllowanceSto Contracts can have multiple storage fields for logical separation: -```fe +```fe ignore pub struct BalanceStorage { pub balances: StorageMap, pub total_supply: u256, diff --git a/src/content/docs/effects/built-in.md b/src/content/docs/effects/built-in.md index 95b81e9..fa0a734 100644 --- a/src/content/docs/effects/built-in.md +++ b/src/content/docs/effects/built-in.md @@ -9,7 +9,7 @@ Unlike some languages with fixed built-in effects, Fe's effects are user-defined Any struct or trait can serve as an effect: -```fe +```fe ignore // Define your own effect type pub struct Logger { pub entries: Vec, @@ -34,7 +34,7 @@ While Fe doesn't prescribe specific built-in effects, certain patterns are commo A context effect provides execution environment information: -```fe +```fe ignore pub struct Ctx { pub caller: u256, pub block_number: u256, @@ -56,7 +56,7 @@ fn get_timestamp() uses Ctx -> u256 { A storage effect represents contract state: -```fe +```fe ignore pub struct TokenStore { pub balances: StorageMap, pub total_supply: u256, @@ -77,7 +77,7 @@ fn mint(to: u256, amount: u256) uses mut TokenStore { A logger effect tracks events: -```fe +```fe ignore pub struct EventLog { // Event logging state } @@ -91,7 +91,7 @@ fn emit_transfer(from: u256, to: u256, amount: u256) uses mut EventLog { A configuration effect provides settings: -```fe +```fe ignore pub struct Config { pub max_transfer: u256, pub fee_rate: u256, @@ -114,7 +114,7 @@ fn calculate_fee(amount: u256) uses Config -> u256 { Real contracts often need multiple effects: -```fe +```fe ignore fn transfer(from: u256, to: u256, amount: u256) uses (Ctx, mut TokenStore, Config, mut EventLog) { @@ -144,7 +144,7 @@ fn transfer(from: u256, to: u256, amount: u256) Effects can be generic: -```fe +```fe ignore pub struct Cache { pub value: T, pub valid: bool, @@ -168,7 +168,7 @@ fn set_cached(value: T) uses mut Cache { Traits can also serve as effects, enabling polymorphism: -```fe +```fe ignore trait Validator { fn is_valid(self, value: u256) -> bool } @@ -186,7 +186,7 @@ When creating effects, consider: Keep effects focused on one responsibility: -```fe +```fe ignore // Good: separate concerns pub struct Balances { ... } pub struct Allowances { ... } @@ -204,7 +204,7 @@ pub struct TokenState { Only require `mut` when necessary: -```fe +```fe ignore // Read-only check fn has_balance(account: u256, amount: u256) uses Balances -> bool { Balances.get(account) >= amount @@ -221,7 +221,7 @@ fn debit(account: u256, amount: u256) uses mut Balances { Name effects to reflect their purpose: -```fe +```fe ignore // Clear purpose pub struct TokenBalances { ... } pub struct AccessControl { ... } diff --git a/src/content/docs/effects/declaring-effects.md b/src/content/docs/effects/declaring-effects.md index 0612ce4..6e95873 100644 --- a/src/content/docs/effects/declaring-effects.md +++ b/src/content/docs/effects/declaring-effects.md @@ -9,7 +9,7 @@ The `uses` clause declares what effects a function or contract requires. This se Add a `uses` clause after the parameter list and before the return type: -```fe +```fe ignore fn function_name() uses EffectType { // function body } @@ -23,7 +23,7 @@ fn with_params(x: u256) uses EffectType -> u256 { The simplest form declares a single effect: -```fe +```fe ignore pub struct Storage { pub data: u256, } @@ -41,7 +41,7 @@ fn write_data(value: u256) uses mut Storage { Give an effect a local name for clearer code: -```fe +```fe ignore fn process() uses store: Storage { // Access via the name 'store' let value = store.data @@ -61,7 +61,7 @@ Named effects are especially useful when: Declare multiple effects using parentheses: -```fe +```fe ignore fn complex_operation() uses (Storage, Logger, Config) { // Has access to all three effects } @@ -69,7 +69,7 @@ fn complex_operation() uses (Storage, Logger, Config) { Combine with names and mutability: -```fe +```fe ignore fn transfer() uses (mut balances: Balances, config: Config, mut log: Logger) { // balances and log are mutable // config is read-only @@ -80,7 +80,7 @@ fn transfer() uses (mut balances: Balances, config: Config, mut log: Logger) { Contracts can declare effects in their definition: -```fe +```fe ignore contract Token uses (mut ctx: Context) { store: TokenStorage, } @@ -88,7 +88,7 @@ contract Token uses (mut ctx: Context) { The contract's effects are available within its recv blocks. Helper functions called from recv blocks receive effects explicitly: -```fe +```fe ignore pub struct TokenStorage { pub balances: StorageMap, } @@ -109,7 +109,7 @@ Effects can be any type or trait: ### Struct Effects -```fe +```fe ignore pub struct AppConfig { pub max_value: u256, pub enabled: bool, @@ -122,7 +122,7 @@ fn check_config() uses AppConfig -> bool { ### Generic Effects -```fe +```fe ignore pub struct Cache { pub value: T, } @@ -148,7 +148,7 @@ fn get_cached() uses Cache -> T { ### Read-Only Helper -```fe +```fe ignore fn get_balance(account: u256) uses Balances -> u256 { Balances.get(account) } @@ -156,7 +156,7 @@ fn get_balance(account: u256) uses Balances -> u256 { ### Mutable Operation -```fe +```fe ignore fn set_balance(account: u256, amount: u256) uses mut Balances { Balances.set(account, amount) } @@ -164,7 +164,7 @@ fn set_balance(account: u256, amount: u256) uses mut Balances { ### Multiple Concerns -```fe +```fe ignore fn logged_transfer(from: u256, to: u256, amount: u256) uses (mut balances: Balances, mut log: TransferLog) { diff --git a/src/content/docs/effects/mutability.md b/src/content/docs/effects/mutability.md index dafd7f1..694b9b0 100644 --- a/src/content/docs/effects/mutability.md +++ b/src/content/docs/effects/mutability.md @@ -9,7 +9,7 @@ Effects distinguish between read-only and mutable access. This distinction is en By default, effects are read-only: -```fe +```fe ignore fn get_value() uses Config -> u256 { Config.value // Can read } @@ -22,7 +22,7 @@ Read-only effects: Attempting to modify a read-only effect is a compile error: -```fe +```fe ignore fn try_modify() uses Config { Config.value = 100 // Error: cannot modify immutable effect } @@ -32,7 +32,7 @@ fn try_modify() uses Config { Add `mut` to allow modification: -```fe +```fe ignore fn set_value(new_value: u256) uses mut Config { Config.value = new_value // Can modify } @@ -51,7 +51,7 @@ When calling a function that requires an effect, the caller must provide a compa A mutable effect can satisfy an immutable requirement: -```fe +```fe ignore fn read_only() uses Data { // reads Data } @@ -65,7 +65,7 @@ fn caller() uses mut Data { An immutable effect cannot satisfy a mutable requirement: -```fe +```fe ignore fn needs_mut() uses mut Data { // modifies Data } @@ -79,7 +79,7 @@ fn caller() uses Data { When providing an effect with `with`, the binding's mutability determines the effect's mutability: -```fe +```fe ignore fn needs_mut() uses mut Counter { Counter.value = Counter.value + 1 } @@ -107,7 +107,7 @@ fn example() { - You want to prevent accidental modification - You're implementing a getter or query -```fe +```fe ignore fn total_supply() uses TokenStore -> u256 { TokenStore.supply } @@ -123,7 +123,7 @@ fn is_valid(amount: u256) uses Config -> bool { - You're implementing a setter or state change - The operation has side effects -```fe +```fe ignore fn mint(amount: u256) uses mut TokenStore { TokenStore.supply = TokenStore.supply + amount } @@ -137,7 +137,7 @@ fn update_config(new_max: u256) uses mut Config { Named effects follow the same rules: -```fe +```fe ignore fn process() uses (data: Data, mut cache: Cache) { // data is read-only // cache is mutable @@ -153,7 +153,7 @@ fn process() uses (data: Data, mut cache: Cache) { A common pattern separates read and write operations: -```fe +```fe ignore pub struct Balances { pub data: StorageMap, } diff --git a/src/content/docs/effects/propagation.md b/src/content/docs/effects/propagation.md index 3e9579c..54ca9e3 100644 --- a/src/content/docs/effects/propagation.md +++ b/src/content/docs/effects/propagation.md @@ -11,7 +11,7 @@ When function A calls function B, and B requires an effect, A must either: 1. Have that effect in its own `uses` clause 2. Provide the effect using a `with` expression -```fe +```fe ignore fn inner() uses Storage { // Uses Storage } @@ -23,7 +23,7 @@ fn outer() uses Storage { If an effect is missing, the compiler reports an error: -```fe +```fe ignore fn inner() uses Storage { // Uses Storage } @@ -37,7 +37,7 @@ fn outer() { The `with` expression provides effects to a scope: -```fe +```fe ignore fn needs_storage() uses Storage { // ... } @@ -53,7 +53,7 @@ fn main() { ### Syntax -```fe +```fe ignore with (EffectType = value) { // Effect is available here } @@ -68,7 +68,7 @@ with (Effect1 = val1, Effect2 = val2) { Effects from `with` are only available inside the block: -```fe +```fe ignore fn example() { let data = Data { value: 0 } @@ -84,7 +84,7 @@ fn example() { Effects propagate through any depth of calls: -```fe +```fe ignore fn level3() uses Config { // Uses Config } @@ -110,7 +110,7 @@ fn entry() { `with` expressions can be nested, creating layered scopes: -```fe +```fe ignore fn needs_both() uses (A, B) { // ... } @@ -134,7 +134,7 @@ fn example() { Or provide multiple effects at once: -```fe +```fe ignore with (A = a, B = b) { needs_both() } @@ -144,7 +144,7 @@ with (A = a, B = b) { Inner scopes can shadow outer effects: -```fe +```fe ignore fn read_value() uses Data -> u256 { Data.value } @@ -169,7 +169,7 @@ fn example() { Functions can have their own effects and also provide additional effects: -```fe +```fe ignore fn helper() uses (Config, mut Logger) { // Needs both effects } @@ -191,7 +191,7 @@ The compiler provides clear error messages for effect issues: ### Missing Effect -```fe +```fe ignore fn needs_data() uses Data { } fn caller() { @@ -203,7 +203,7 @@ fn caller() { ### Mutability Mismatch -```fe +```fe ignore fn needs_mut() uses mut Data { } fn caller() uses Data { diff --git a/src/content/docs/effects/storage.md b/src/content/docs/effects/storage.md index cd82c4f..c7af82e 100644 --- a/src/content/docs/effects/storage.md +++ b/src/content/docs/effects/storage.md @@ -9,7 +9,7 @@ Contract storage in Fe is modeled through effects. This makes storage access exp Define storage as a struct that serves as an effect: -```fe +```fe ignore pub struct TokenStorage { pub balances: StorageMap, pub total_supply: u256, @@ -18,7 +18,7 @@ pub struct TokenStorage { Functions that need storage access declare it explicitly: -```fe +```fe ignore fn get_balance(account: u256) uses TokenStorage -> u256 { TokenStorage.balances.get(account) } @@ -32,7 +32,7 @@ fn get_total_supply() uses TokenStorage -> u256 { Use `mut` when the function modifies storage: -```fe +```fe ignore // Read-only access fn get_balance(account: u256) uses TokenStorage -> u256 { TokenStorage.balances.get(account) @@ -54,7 +54,7 @@ fn mint(to: u256, amount: u256) uses mut TokenStorage { Contracts declare storage fields and provide them as effects to recv blocks: -```fe +```fe ignore pub struct TokenStorage { pub balances: StorageMap, pub total_supply: u256, @@ -67,7 +67,7 @@ contract Token { Inside recv blocks, use `with` to provide the storage effect: -```fe +```fe ignore // Helper functions with explicit storage effects fn do_transfer(from: u256, to: u256, amount: u256) uses mut TokenStorage { let from_balance = TokenStorage.balances.get(from) @@ -86,7 +86,7 @@ fn get_balance(account: u256) uses TokenStorage -> u256 { Separate different concerns into distinct storage effects: -```fe +```fe ignore pub struct Balances { pub data: StorageMap, } @@ -137,7 +137,7 @@ Making storage access explicit provides: ### Clear Dependencies -```fe +```fe ignore // This signature tells you exactly what storage is accessed fn do_transfer(from: u256, to: u256, amount: u256) uses mut Balances { // ... @@ -146,7 +146,7 @@ fn do_transfer(from: u256, to: u256, amount: u256) uses mut Balances { ### Enforced Separation -```fe +```fe ignore // This function cannot accidentally modify Allowances fn transfer(from: u256, to: u256, amount: u256) uses mut Balances { // Compiler error if you try to access Allowances here @@ -155,7 +155,7 @@ fn transfer(from: u256, to: u256, amount: u256) uses mut Balances { ### Easy Testing -```fe +```fe ignore fn test_transfer() { let mut balances = Balances { data: mock_storage_map() diff --git a/src/content/docs/effects/what-are-effects.md b/src/content/docs/effects/what-are-effects.md index 70e600b..7e6588d 100644 --- a/src/content/docs/effects/what-are-effects.md +++ b/src/content/docs/effects/what-are-effects.md @@ -14,7 +14,7 @@ In many languages, functions can access global state, modify storage, or call ex - **Testing difficulties**: Mocking hidden dependencies is awkward - **Security risks**: In smart contracts, hidden state access can lead to vulnerabilities -```fe +```fe ignore // In languages without effects, this signature tells you nothing // about what resources the function accesses fn transfer(from: u256, to: u256, amount: u256) @@ -24,7 +24,7 @@ fn transfer(from: u256, to: u256, amount: u256) Fe's effect system requires functions to declare their capabilities upfront using the `uses` clause: -```fe +```fe ignore fn transfer(from: u256, to: u256, amount: u256) uses mut Balances { // This function can only access Balances // and can modify it (mut) @@ -40,7 +40,7 @@ Now the function signature tells you exactly what it can do: Here's how effects work in practice: -```fe +```fe ignore // Define an effect type pub struct Counter { pub value: u256, diff --git a/src/content/docs/effects/why-effects-matter.md b/src/content/docs/effects/why-effects-matter.md index 32db8cc..a060590 100644 --- a/src/content/docs/effects/why-effects-matter.md +++ b/src/content/docs/effects/why-effects-matter.md @@ -11,7 +11,7 @@ Effects provide significant benefits for smart contract development. They make c Every function declares exactly what it can access: -```fe +```fe ignore fn transfer(from: u256, to: u256, amount: u256) uses mut Balances { // Can ONLY modify Balances // Cannot access Allowances, Config, or anything else @@ -24,7 +24,7 @@ This makes security audits easier—you know a function's blast radius just by r Functions only get the capabilities they need: -```fe +```fe ignore // This function can only read fn get_balance(account: u256) uses Balances -> u256 { Balances.get(account) @@ -42,7 +42,7 @@ A bug in `get_balance` cannot corrupt state because it lacks mutation capability Unlike languages where any function might access global state, Fe functions can only access what they declare: -```fe +```fe ignore // In Fe, this function signature guarantees // it cannot access any external state fn calculate_fee(amount: u256, rate: u256) -> u256 { @@ -58,7 +58,7 @@ If a function has no `uses` clause, it's a pure computation. Effects make mocking straightforward: -```fe +```fe ignore fn process_payment(amount: u256) uses (Config, mut Balances, mut Logger) { let fee = amount * Config.fee_rate / 10000 @@ -86,7 +86,7 @@ fn test_payment() { Test functions in isolation by providing only the effects they need: -```fe +```fe ignore fn validate_transfer(from: u256, amount: u256) uses Balances -> bool { Balances.get(from) >= amount } @@ -107,7 +107,7 @@ fn test_validate_transfer() { Pure functions (no effects) need no setup at all: -```fe +```fe ignore fn calculate_shares(amount: u256, total: u256, supply: u256) -> u256 { if total == 0 { amount @@ -127,7 +127,7 @@ fn test_calculate_shares() { ### Function Signatures Tell the Story -```fe +```fe ignore // Just by reading this signature, you know: // - It needs caller context // - It reads token store @@ -141,7 +141,7 @@ fn transfer(to: u256, amount: u256) Effects create explicit dependency graphs: -```fe +```fe ignore fn outer() uses (A, B) { inner1() // Must provide effects A needs inner2() // Must provide effects B needs @@ -157,7 +157,7 @@ You can trace exactly how effects flow through your code. When refactoring, the compiler catches missing effects: -```fe +```fe ignore // Before: function was pure fn calculate(x: u256) -> u256 { x * 2 @@ -183,7 +183,7 @@ All effect checking happens at compile time: ### No Runtime Surprises -```fe +```fe ignore fn risky_operation() uses mut CriticalState { // ... } diff --git a/src/content/docs/events/abi.md b/src/content/docs/events/abi.md index 99058a5..de81d7c 100644 --- a/src/content/docs/events/abi.md +++ b/src/content/docs/events/abi.md @@ -25,7 +25,7 @@ Log Entry Topic 0 is always the keccak256 hash of the event signature: -```fe +```fe ignore struct Transfer { #[indexed] from: u256, @@ -45,7 +45,7 @@ This matches Solidity's event encoding, ensuring tools recognize your events. Each `#[indexed]` field becomes a topic: -```fe +```fe ignore struct Transfer { #[indexed] from: u256, // → topics[1] @@ -68,7 +68,7 @@ When emitting `Transfer { from: 0x123, to: 0x456, amount: 1000 }`: Fields without `#[indexed]` are ABI-encoded into the data section: -```fe +```fe ignore struct Swap { #[indexed] sender: u256, @@ -99,7 +99,7 @@ Fe types map to Solidity/ABI types: To emit ERC20-compatible events: -```fe +```fe ignore // ERC20 Transfer event // Solidity: event Transfer(address indexed from, address indexed to, uint256 value) struct Transfer { @@ -177,7 +177,7 @@ Note: The signature includes all parameter types, not just indexed ones. To emit events compatible with existing Solidity contracts: -```fe +```fe ignore // Match Solidity: event Transfer(address indexed from, address indexed to, uint256 value) struct Transfer { #[indexed] @@ -211,7 +211,7 @@ Large values in indexed fields are hashed: Match established conventions: -```fe +```fe ignore // ERC20 standard names struct Transfer { ... } struct Approval { ... } @@ -225,7 +225,7 @@ struct ApprovalForAll { ... } Field order affects the signature: -```fe +```fe ignore // Order: indexed fields first, then data fields struct Transfer { #[indexed] @@ -241,7 +241,7 @@ struct Transfer { Include signatures in your documentation: -```fe +```fe ignore /// Transfer event /// Signature: Transfer(uint256,uint256,uint256) /// Topic 0: 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef diff --git a/src/content/docs/events/emitting.md b/src/content/docs/events/emitting.md index 842c30a..971e23b 100644 --- a/src/content/docs/events/emitting.md +++ b/src/content/docs/events/emitting.md @@ -9,7 +9,7 @@ Events are emitted using a Log effect, which records data to the blockchain that Emit an event through a Log effect: -```fe +```fe ignore pub struct EventLog { // Event logging capability } @@ -33,7 +33,7 @@ The `emit` method takes an event struct instance and records it to the blockchai Events are typically emitted within message handlers: -```fe +```fe ignore pub struct TokenStorage { pub balances: StorageMap, } @@ -72,7 +72,7 @@ fn do_transfer(from: u256, to: u256, amount: u256) A critical pattern: emit events after state changes succeed, not before: -```fe +```fe ignore fn transfer(from: u256, to: u256, amount: u256) uses (mut TokenStorage, mut EventLog) -> bool @@ -100,7 +100,7 @@ This ensures events reflect actual state changes. In contracts, bind the EventLog effect like other storage: -```fe +```fe ignore pub struct TokenStorage { pub balances: StorageMap, } @@ -138,7 +138,7 @@ contract Token { Emit different event types from the same handler: -```fe +```fe ignore struct Transfer { #[indexed] from: u256, @@ -189,7 +189,7 @@ fn transfer_from(spender: u256, from: u256, to: u256, amount: u256) Emit when persistent state changes: -```fe +```fe ignore fn mint(to: u256, amount: u256) uses (mut TokenStorage, mut EventLog) { TokenStorage.balances.set(to, TokenStorage.balances.get(to) + amount) TokenStorage.total_supply = TokenStorage.total_supply + amount @@ -209,7 +209,7 @@ fn burn(from: u256, amount: u256) uses (mut TokenStorage, mut EventLog) { Emit for ownership and configuration changes: -```fe +```fe ignore struct OwnershipTransferred { #[indexed] previous_owner: u256, @@ -234,7 +234,7 @@ fn transfer_ownership(new_owner: u256) Occasionally emit for important queries (use sparingly): -```fe +```fe ignore struct BalanceChecked { #[indexed] account: u256, @@ -248,7 +248,7 @@ struct BalanceChecked { Create helper functions for common events: -```fe +```fe ignore fn emit_transfer(from: u256, to: u256, amount: u256) uses mut EventLog { EventLog.emit(Transfer { from, to, amount }) } @@ -272,7 +272,7 @@ fn transfer(from: u256, to: u256, amount: u256) Only emit when something meaningful happens: -```fe +```fe ignore fn set_approval(owner: u256, spender: u256, new_amount: u256) uses (mut TokenStorage, mut EventLog) { diff --git a/src/content/docs/events/event-structs.md b/src/content/docs/events/event-structs.md index ec67739..58ffb2a 100644 --- a/src/content/docs/events/event-structs.md +++ b/src/content/docs/events/event-structs.md @@ -9,7 +9,7 @@ Events in Fe are defined as structs with special attributes. When emitted, they Define an event as a struct: -```fe +```fe ignore struct Transfer { from: u256, to: u256, @@ -29,7 +29,7 @@ These are regular structs that become events when emitted through the Log effect The `#[indexed]` attribute marks fields that should become EVM log topics, making them filterable: -```fe +```fe ignore struct Transfer { #[indexed] from: u256, @@ -48,7 +48,7 @@ With indexed fields: Indexed fields enable efficient queries: -```fe +```fe ignore struct Transfer { #[indexed] from: u256, // Filter: "all transfers FROM this address" @@ -71,7 +71,7 @@ The EVM allows at most 4 topics per log: This means you can have at most 3 indexed fields: -```fe +```fe ignore // Valid: 3 indexed fields struct ComplexEvent { #[indexed] @@ -102,7 +102,7 @@ struct TooManyIndexed { Standard token events follow ERC20/ERC721 conventions: -```fe +```fe ignore // ERC20 Transfer struct Transfer { #[indexed] @@ -136,7 +136,7 @@ struct NftTransfer { Events for contract administration: -```fe +```fe ignore struct OwnershipTransferred { #[indexed] previous_owner: u256, @@ -157,7 +157,7 @@ struct Unpaused { Events recording state changes: -```fe +```fe ignore struct Deposit { #[indexed] account: u256, @@ -183,7 +183,7 @@ struct ConfigUpdated { Each event should represent one logical occurrence: -```fe +```fe ignore // Good: specific events struct Minted { #[indexed] @@ -209,7 +209,7 @@ struct SupplyChanged { Index fields you'll filter by: -```fe +```fe ignore struct Trade { #[indexed] trader: u256, // Often filtered by trader @@ -226,7 +226,7 @@ struct Trade { Events should be self-contained for off-chain processing: -```fe +```fe ignore struct Swap { #[indexed] sender: u256, diff --git a/src/content/docs/events/log-effect.md b/src/content/docs/events/log-effect.md index 11d9ebf..d674db3 100644 --- a/src/content/docs/events/log-effect.md +++ b/src/content/docs/events/log-effect.md @@ -9,7 +9,7 @@ In Fe, logging is an effect—a capability that must be explicitly declared. Thi Unlike languages where logging is implicit, Fe treats it as a tracked capability: -```fe +```fe ignore pub struct EventLog {} struct Transfer { @@ -43,13 +43,13 @@ The function signature tells you exactly what the function can do. Define a Log effect as a struct: -```fe +```fe ignore pub struct EventLog {} ``` Use it in function signatures: -```fe +```fe ignore // Read-only logging isn't meaningful, so always use mut fn emit_transfer(from: u256, to: u256, amount: u256) uses mut EventLog { EventLog.emit(Transfer { from, to, amount }) @@ -62,7 +62,7 @@ fn emit_transfer(from: u256, to: u256, amount: u256) uses mut EventLog { Function signatures reveal side effects: -```fe +```fe ignore // Looking at this signature, you know: // - It reads Config (immutable) // - It modifies Balances (mutable) @@ -78,7 +78,7 @@ In Solidity, you'd need to read the implementation to know if events are emitted Mock or replace the Log effect in tests: -```fe +```fe ignore pub struct MockEventLog { pub events: Vec, // Track emitted events } @@ -100,7 +100,7 @@ fn test_transfer() { Compose functions while controlling which can log: -```fe +```fe ignore // Internal helper - no logging fn update_balance(account: u256, delta: i256) uses mut Balances { // Pure state update, no events @@ -119,7 +119,7 @@ fn deposit(account: u256, amount: u256) Functions calling logging functions must declare the effect: -```fe +```fe ignore fn emit_transfer(from: u256, to: u256, amount: u256) uses mut EventLog { EventLog.emit(Transfer { from, to, amount }) } @@ -149,7 +149,7 @@ fn broken_transfer(from: u256, to: u256, amount: u256) Contracts provide the Log effect via `with`: -```fe +```fe ignore pub struct EventLog {} contract Token { @@ -170,7 +170,7 @@ contract Token { Use different Log effects for different event categories: -```fe +```fe ignore pub struct TransferLog {} pub struct AdminLog {} @@ -213,7 +213,7 @@ This gives fine-grained control over which functions can emit which events. Often storage and its events are paired: -```fe +```fe ignore pub struct TokenStorage { pub balances: StorageMap, pub total_supply: u256, @@ -234,7 +234,7 @@ fn mint(to: u256, amount: u256) Some functions exist solely to emit events: -```fe +```fe ignore fn log_debug(message: u256) uses mut DebugLog { DebugLog.emit(DebugMessage { value: message }) } @@ -244,7 +244,7 @@ fn log_debug(message: u256) uses mut DebugLog { Make logging optional by separating concerns: -```fe +```fe ignore // Core logic - no logging fn compute_fee(amount: u256) uses Config -> u256 { amount * Config.fee_rate / 10000 diff --git a/src/content/docs/examples/erc20.md b/src/content/docs/examples/erc20.md index 4734528..e9e3fad 100644 --- a/src/content/docs/examples/erc20.md +++ b/src/content/docs/examples/erc20.md @@ -264,6 +264,50 @@ struct ApprovalEvent { spender: Address, value: u256, } + +// stubs for missing std lib stuff +extern { + fn assert(_: bool, _: String<64>) +} + +pub struct Address { inner: u256 } +impl Address { + pub fn zero() -> Self { + Address { inner: 0 } + } +} +impl core::ops::Eq for Address { + fn eq(self, _ other: Address) -> bool { + self.inner == other.inner + } +} + +pub struct Map {} +impl Map { + pub fn new() -> Self { + Map {} + } +} +impl core::ops::Index for Map { + type Output = V + fn index(self, _ key: K) -> V { + todo() + } +} + +pub struct Ctx {} +impl Ctx { + pub fn caller(self) -> Address { + todo() + } +} + +pub struct Log {} +impl Log { + pub fn emit(self, _ event: T) { + todo() + } +} ``` ## Walkthrough @@ -272,7 +316,7 @@ struct ApprovalEvent { The contract declares effects at the contract level: -```fe +```fe ignore pub contract CoolCoin uses (mut ctx: Ctx, mut log: Log) { mut store: TokenStore, mut auth: AccessControl, @@ -288,7 +332,7 @@ Key points: The contract uses two storage structs: -```fe +```fe ignore struct TokenStore { total_supply: u256, balances: Map, @@ -311,7 +355,7 @@ pub struct AccessControl { Messages define the external interface with ABI-compatible selectors: -```fe +```fe ignore msg Erc20 { #[selector = 0x06fdde03] Name -> String<32>, @@ -328,7 +372,7 @@ Each selector matches the standard Solidity function selector, ensuring ABI comp ### The init Block -```fe +```fe ignore init(initial_supply: u256, owner: Address) uses (mut store, mut auth, mut ctx, mut log) { @@ -350,7 +394,7 @@ The constructor: Each handler declares its specific effect requirements: -```fe +```fe ignore recv Erc20 { Transfer { to, amount } -> bool uses (ctx, mut store, mut log) { transfer(from: ctx.caller(), to, amount) @@ -372,7 +416,7 @@ Notice: Core logic is extracted into standalone functions: -```fe +```fe ignore fn transfer(from: Address, to: Address, amount: u256) uses (mut store: TokenStore, mut log: Log) { @@ -401,7 +445,7 @@ The `mint` and `burn` functions follow the same pattern, using `Address::zero()` Events are structs with `#[indexed]` fields for filtering: -```fe +```fe ignore struct TransferEvent { #[indexed] from: Address, @@ -421,7 +465,7 @@ struct ApprovalEvent { Events are emitted via the Log effect: -```fe +```fe ignore log.emit(TransferEvent { from, to, value: amount }) ``` @@ -429,7 +473,7 @@ log.emit(TransferEvent { from, to, value: amount }) Role-based access control is implemented as a struct with methods: -```fe +```fe ignore const MINTER: u256 = 1 const BURNER: u256 = 2 @@ -446,7 +490,7 @@ impl AccessControl { Usage in handlers: -```fe +```fe ignore Mint { to, amount } -> bool uses (ctx, mut store, mut log, auth) { auth.require(role: MINTER) mint(to, amount) diff --git a/src/content/docs/foundations/control-flow.md b/src/content/docs/foundations/control-flow.md index cc5a7d3..087508a 100644 --- a/src/content/docs/foundations/control-flow.md +++ b/src/content/docs/foundations/control-flow.md @@ -11,7 +11,7 @@ Control flow constructs determine the order in which code executes. Fe provides Use `if` to execute code based on a condition: -```fe +```fe ignore if balance > 0 { process_withdrawal() } @@ -21,7 +21,7 @@ if balance > 0 { Add an `else` branch for when the condition is false: -```fe +```fe ignore if balance >= amount { withdraw(amount) } else { @@ -33,7 +33,7 @@ if balance >= amount { Chain multiple conditions with `else if`: -```fe +```fe ignore if score >= 90 { grade = "A" } else if score >= 80 { @@ -49,7 +49,7 @@ if score >= 90 { In Fe, `if` is an expression that returns a value. Both branches must return the same type: -```fe +```fe ignore let max = if a > b { a } else { b } let status = if is_active { @@ -61,7 +61,7 @@ let status = if is_active { This is useful for concise conditional assignment: -```fe +```fe ignore let fee = if is_premium { 0 } else { calculate_fee(amount) } ``` @@ -71,7 +71,7 @@ let fee = if is_premium { 0 } else { calculate_fee(amount) } The `match` expression provides powerful pattern matching: -```fe +```fe ignore match value { 0 => handle_zero() 1 => handle_one() @@ -81,7 +81,7 @@ match value { Like `if`, `match` is an expression that returns a value: -```fe +```fe ignore let description = match count { 0 => "none" 1 => "one" @@ -94,7 +94,7 @@ let description = match count { Match on enum variants to handle different cases: -```fe +```fe ignore enum Status { Pending Approved @@ -112,7 +112,7 @@ let message = match status { Use patterns to destructure and bind values: -```fe +```fe ignore match point { (0, 0) => "origin" (0, y) => "on y-axis" @@ -123,7 +123,7 @@ match point { Match on struct fields: -```fe +```fe ignore match user { User { active: true, role } => process_active(role) User { active: false, .. } => handle_inactive() @@ -134,7 +134,7 @@ match user { Use `_` to match any value you don't need: -```fe +```fe ignore match result { Ok(value) => use_value(value) Err(_) => handle_error() // Ignore the specific error @@ -145,7 +145,7 @@ match result { Match expressions must be exhaustive—every possible value must be handled. The wildcard `_` is often used as a catch-all: -```fe +```fe ignore match day { 1 => "Monday" 2 => "Tuesday" @@ -164,7 +164,7 @@ match day { Use `for` to iterate over a collection: -```fe +```fe ignore for item in items { process(item) } @@ -174,7 +174,7 @@ for item in items { Destructure elements while iterating: -```fe +```fe ignore for (index, value) in indexed_items { store_at(index, value) } @@ -190,7 +190,7 @@ for User { name, balance } in users { Use `while` for condition-based loops: -```fe +```fe ignore while count > 0 { process_next() count = count - 1 @@ -199,7 +199,7 @@ while count > 0 { A common pattern for indefinite loops: -```fe +```fe ignore while true { if should_stop() { break @@ -214,7 +214,7 @@ while true { Use `break` to exit a loop early: -```fe +```fe ignore for item in items { if item.is_target() { found = item @@ -227,7 +227,7 @@ for item in items { Use `continue` to skip to the next iteration: -```fe +```fe ignore for item in items { if item.should_skip() { continue // Skip this item @@ -238,7 +238,7 @@ for item in items { ### Combining Break and Continue -```fe +```fe ignore let mut sum: u256 = 0 for value in values { // Skip negative values @@ -261,7 +261,7 @@ for value in values { Use `return` to exit a function early: -```fe +```fe ignore fn find_user(id: u256) -> Option { if id == 0 { return Option::None // Early return for invalid ID @@ -277,7 +277,7 @@ fn find_user(id: u256) -> Option { Return a specific value from anywhere in the function: -```fe +```fe ignore fn calculate_discount(amount: u256, is_member: bool) -> u256 { if amount < 100 { return 0 // No discount for small amounts @@ -299,7 +299,7 @@ Fe functions return the value of their last expression implicitly. Use explicit - Multiple exit points - Clarity in complex functions -```fe +```fe ignore // Implicit return - last expression is the return value fn double(x: u256) -> u256 { x * 2 diff --git a/src/content/docs/foundations/functions.md b/src/content/docs/foundations/functions.md index 2eae4a3..d06a222 100644 --- a/src/content/docs/foundations/functions.md +++ b/src/content/docs/foundations/functions.md @@ -9,7 +9,7 @@ Functions are the building blocks of Fe programs. They encapsulate reusable logi Declare a function using the `fn` keyword: -```fe +```fe ignore fn greet() { // function body } @@ -17,7 +17,7 @@ fn greet() { A function with parameters and a return type: -```fe +```fe ignore fn add(a: u256, b: u256) -> u256 { a + b } @@ -29,7 +29,7 @@ fn add(a: u256, b: u256) -> u256 { Parameters are declared with a name and type: -```fe +```fe ignore fn process(value: u256, flag: bool) { // use value and flag } @@ -39,7 +39,7 @@ fn process(value: u256, flag: bool) { Fe supports **labeled parameters** for improved call-site clarity. A label is the name used when calling the function, while the parameter name is used inside the function body: -```fe +```fe ignore fn transfer(from sender: u256, to recipient: u256, amount: u256) { // Inside the function, use: sender, recipient, amount } @@ -54,7 +54,7 @@ This makes function calls self-documenting and reduces errors when functions hav Use `_` as the label when you don't want to require a label at the call site: -```fe +```fe ignore fn square(_ x: u256) -> u256 { x * x } @@ -65,7 +65,7 @@ let result = square(5) You can mix labeled and unlabeled parameters: -```fe +```fe ignore fn create_point(_ x: u256, _ y: u256, named: bool) -> Point { // x and y are positional, named requires a label } @@ -78,7 +78,7 @@ create_point(10, 20, named: true) Use `mut` to declare a parameter that can be modified within the function: -```fe +```fe ignore fn increment(mut value: u256) -> u256 { value = value + 1 value @@ -91,7 +91,7 @@ Note that this creates a mutable local copy; it doesn't modify the caller's vari Functions that operate on a type instance use `self` as their first parameter, making them **methods**: -```fe +```fe ignore struct Counter { value: u256 } @@ -111,7 +111,7 @@ impl Counter { Methods are called using dot notation: -```fe +```fe ignore let mut counter = Counter { value: 0 } counter.increment() let current = counter.get() @@ -128,7 +128,7 @@ let current = counter.get() Specify a return type with `->` after the parameters: -```fe +```fe ignore fn calculate(x: u256) -> u256 { x * 2 } @@ -138,7 +138,7 @@ fn calculate(x: u256) -> u256 { The last expression in a function is implicitly returned (without a semicolon): -```fe +```fe ignore fn double(x: u256) -> u256 { x * 2 // implicitly returned } @@ -148,7 +148,7 @@ fn double(x: u256) -> u256 { Use `return` for early returns or explicit clarity: -```fe +```fe ignore fn abs(x: i256) -> i256 { if x < 0 { return -x @@ -161,7 +161,7 @@ fn abs(x: i256) -> i256 { Functions without `->` return the unit type (similar to `void` in other languages): -```fe +```fe ignore fn log_value(value: u256) { // no return value } @@ -175,7 +175,7 @@ Functions are **private by default**, accessible only within their module. Use `pub` to make a function accessible from other modules: -```fe +```fe ignore pub fn public_function() { // accessible from other modules } @@ -194,7 +194,7 @@ In Fe, contracts cannot contain regular functions. Contracts only have: Functions are defined separately—either as free-floating functions or as methods on structs. Code inside `recv` blocks can call these external functions: -```fe +```fe ignore // Free-floating helper function fn calculate_fee(amount: u256) -> u256 { amount / 100 @@ -232,7 +232,7 @@ This separation keeps contracts focused on state and message handling, while log Functions can be generic over types using angle brackets: -```fe +```fe ignore fn identity(value: T) -> T { value } @@ -240,7 +240,7 @@ fn identity(value: T) -> T { With trait bounds: -```fe +```fe ignore fn print_value(value: T) { // T must implement Display } @@ -252,7 +252,7 @@ For comprehensive coverage of generics and trait bounds, see the [Traits & Gener Fe uses an effect system to track side effects. Functions declare their effects with the `uses` clause: -```fe +```fe ignore fn read_storage() uses Storage { // can read from storage } diff --git a/src/content/docs/foundations/operators.md b/src/content/docs/foundations/operators.md index 69ae68b..e0b1b1b 100644 --- a/src/content/docs/foundations/operators.md +++ b/src/content/docs/foundations/operators.md @@ -18,7 +18,7 @@ Arithmetic operators work on numeric types. | `%` | Modulo (remainder) | `a % b` | | `**` | Exponentiation | `a ** b` | -```fe +```fe ignore let sum = 10 + 5 // 15 let diff = 10 - 5 // 5 let product = 10 * 5 // 50 @@ -31,7 +31,7 @@ let power = 2 ** 8 // 256 Division between integers performs integer division, truncating toward zero: -```fe +```fe ignore let result = 7 / 2 // 3, not 3.5 let neg = -7 / 2 // -3 ``` @@ -49,7 +49,7 @@ Comparison operators return `bool` values. | `>` | Greater than | `a > b` | | `>=` | Greater than or equal | `a >= b` | -```fe +```fe ignore let x = 5 let y = 10 @@ -71,7 +71,7 @@ Logical operators work on `bool` values. | `\|\|` | Logical OR | `a \|\| b` | | `!` | Logical NOT | `!a` | -```fe +```fe ignore let a = true let b = false @@ -87,7 +87,7 @@ Logical operators use short-circuit evaluation: - `&&` stops evaluating if the left operand is `false` - `||` stops evaluating if the left operand is `true` -```fe +```fe ignore // If is_valid is false, expensive_check() won't be called if is_valid && expensive_check() { // ... @@ -110,7 +110,7 @@ Bitwise operators manipulate individual bits of integer values. | `<<` | Left shift | `a << n` | | `>>` | Right shift | `a >> n` | -```fe +```fe ignore let a: u8 = 0b1100 let b: u8 = 0b1010 @@ -125,7 +125,7 @@ let shifted_right = a >> 2 // 0b0011 (3) ### Common Bitwise Patterns -```fe +```fe ignore // Check if a bit is set let has_flag = (value & FLAG) != 0 @@ -150,7 +150,7 @@ Unary operators take a single operand. | `!` | Logical NOT | `!flag` | | `~` | Bitwise NOT | `~bits` | -```fe +```fe ignore let x: i256 = 42 let negative = -x // -42 let positive = +x // 42 (no change) @@ -168,7 +168,7 @@ let flipped = ~bits // 0b0000_1111 Use `=` to assign a value to a mutable variable: -```fe +```fe ignore let mut x = 10 x = 20 // x is now 20 ``` @@ -190,7 +190,7 @@ Compound assignment operators combine an operation with assignment: | `<<=` | `x = x << y` | Left shift and assign | | `>>=` | `x = x >> y` | Right shift and assign | -```fe +```fe ignore let mut count = 10 count += 5 // count is now 15 count -= 3 // count is now 12 @@ -210,7 +210,7 @@ Fe is an expression-oriented language. Most constructs produce values. A block `{ }` is an expression that evaluates to its last expression: -```fe +```fe ignore let result = { let a = 10 let b = 20 @@ -223,7 +223,7 @@ let result = { Call functions with parentheses: -```fe +```fe ignore let value = compute(10, 20) let result = process(data, flag: true) // with labeled argument ``` @@ -232,7 +232,7 @@ let result = process(data, flag: true) // with labeled argument Call methods on values using dot notation: -```fe +```fe ignore let length = my_string.len() let doubled = counter.multiply(by: 2) ``` @@ -241,7 +241,7 @@ let doubled = counter.multiply(by: 2) Access struct fields with dot notation: -```fe +```fe ignore let x = point.x let name = user.profile.name ``` @@ -250,7 +250,7 @@ let name = user.profile.name Access array and tuple elements by index: -```fe +```fe ignore let first = arr[0] let third = arr[2] @@ -262,7 +262,7 @@ let y = tuple.1 Create tuples with parentheses: -```fe +```fe ignore let point = (10, 20) let triple = (1, true, "hello") let unit = () // empty tuple (unit type) @@ -272,7 +272,7 @@ let unit = () // empty tuple (unit type) Create arrays with brackets: -```fe +```fe ignore let numbers = [1, 2, 3, 4, 5] let flags = [true, false, true] @@ -284,7 +284,7 @@ let zeros = [0; 10] // array of 10 zeros `if` is an expression that returns a value: -```fe +```fe ignore let max = if a > b { a } else { b } let description = if count == 0 { @@ -300,7 +300,7 @@ let description = if count == 0 { `match` is an expression for pattern matching: -```fe +```fe ignore let result = match value { 0 => "zero" 1 => "one" @@ -312,7 +312,7 @@ let result = match value { Use parentheses to control evaluation order: -```fe +```fe ignore let result = (a + b) * c let complex = ((x + y) * z) / w ``` @@ -339,7 +339,7 @@ Operators are evaluated according to their precedence. Higher precedence operato When in doubt, use parentheses to make your intent clear: -```fe +```fe ignore // These are equivalent, but the second is clearer let result = a + b * c let result = a + (b * c) diff --git a/src/content/docs/foundations/primitive-types.md b/src/content/docs/foundations/primitive-types.md index 8376612..0d0542e 100644 --- a/src/content/docs/foundations/primitive-types.md +++ b/src/content/docs/foundations/primitive-types.md @@ -9,14 +9,14 @@ Fe provides a set of primitive types that form the foundation for all data manip The `bool` type represents a logical value that can be either `true` or `false`. -```fe +```fe ignore let is_active: bool = true let has_permission: bool = false ``` Booleans are commonly used in conditional expressions and control flow: -```fe +```fe ignore if is_active { // do something } @@ -42,7 +42,7 @@ Unsigned integers can only represent non-negative values (zero and positive numb | `u256` | 256 | 0 | 2²⁵⁶ - 1 | | `usize` | * | 0 | platform-dependent | -```fe +```fe ignore let small: u8 = 255 let balance: u256 = 1000000000000000000 // 1 ETH in wei let count: u32 = 42 @@ -62,7 +62,7 @@ Signed integers can represent both negative and positive values, using two's com | `i256` | 256 | -2²⁵⁵ | 2²⁵⁵ - 1 | | `isize` | * | platform-dependent | platform-dependent | -```fe +```fe ignore let temperature: i32 = -10 let offset: i256 = -500 ``` @@ -77,7 +77,7 @@ The Ethereum Virtual Machine (EVM) natively operates on 256-bit words. This mean For most smart contract development, prefer `u256` for unsigned values and `i256` for signed values unless you have a specific reason to use smaller types. -```fe +```fe ignore // Recommended for most EVM operations let amount: u256 = 1000 let price: u256 = 500 @@ -94,7 +94,7 @@ Fe supports several formats for writing numeric literals: Standard base-10 numbers: -```fe +```fe ignore let x = 42 let large = 1000000 ``` @@ -103,7 +103,7 @@ let large = 1000000 Use underscores to make large numbers more readable. Underscores are ignored by the compiler: -```fe +```fe ignore let wei_per_eth = 1_000_000_000_000_000_000 let million = 1_000_000 ``` @@ -112,7 +112,7 @@ let million = 1_000_000 Prefix with `0x` or `0X` for base-16 numbers. Useful for addresses, hashes, and bit patterns: -```fe +```fe ignore let color = 0xff5733 let mask = 0xFFFFFFFF let address_value = 0x742d35Cc6634C0532925a3b844Bc9e7595f5e123 @@ -122,7 +122,7 @@ let address_value = 0x742d35Cc6634C0532925a3b844Bc9e7595f5e123 Prefix with `0b` or `0B` for base-2 numbers. Useful for bit flags and masks: -```fe +```fe ignore let flags = 0b1010 let permission_mask = 0b11110000 ``` @@ -131,7 +131,7 @@ let permission_mask = 0b11110000 Prefix with `0o` or `0O` for base-8 numbers: -```fe +```fe ignore let file_mode = 0o755 let octal_value = 0o177 ``` @@ -140,14 +140,14 @@ let octal_value = 0o177 The `String` type represents text data. -```fe +```fe ignore let greeting: String = "Hello, Fe!" let empty: String = "" ``` String literals are enclosed in double quotes. Escape sequences can be used for special characters: -```fe +```fe ignore let with_newline = "Line 1\nLine 2" let with_quote = "She said \"Hello\"" ``` @@ -156,7 +156,7 @@ let with_quote = "She said \"Hello\"" Fe can often infer types from context, so explicit type annotations aren't always required: -```fe +```fe ignore let x = 42 // inferred as integer type let flag = true // inferred as bool let name = "Alice" // inferred as String @@ -164,7 +164,7 @@ let name = "Alice" // inferred as String However, explicit type annotations are recommended when the intended type isn't obvious or when you need a specific integer size: -```fe +```fe ignore let amount: u256 = 100 // explicitly u256, not inferred default let small: u8 = 50 // explicitly u8 for storage efficiency ``` @@ -175,7 +175,7 @@ Fe's primitive types cover the fundamentals, but EVM-specific types like `Addres Currently, EVM addresses are represented as `u256` values: -```fe +```fe ignore use core::intrinsic::caller fn get_sender() -> u256 { diff --git a/src/content/docs/foundations/variables.md b/src/content/docs/foundations/variables.md index 2dce908..0a7e9dd 100644 --- a/src/content/docs/foundations/variables.md +++ b/src/content/docs/foundations/variables.md @@ -9,7 +9,7 @@ Variables are fundamental to any program. Fe provides a straightforward yet powe Use the `let` keyword to declare a variable: -```fe +```fe ignore let x = 42 let name = "Alice" let is_active = true @@ -19,7 +19,7 @@ let is_active = true You can explicitly specify a variable's type using a colon followed by the type: -```fe +```fe ignore let x: u256 = 42 let name: String = "Alice" let balance: u128 = 1000 @@ -31,7 +31,7 @@ Type annotations are optional when the compiler can infer the type from the valu - The type isn't obvious from context - You want to document the intended type for clarity -```fe +```fe ignore // Without annotation - type is inferred let count = 100 @@ -43,7 +43,7 @@ let small_count: u8 = 100 You can declare a variable without immediately initializing it: -```fe +```fe ignore let x: u256 // ... later in the code x = 42 @@ -55,7 +55,7 @@ However, Fe will ensure the variable is initialized before it's used. In Fe, variables are **immutable by default**. Once a value is bound to a name, it cannot be changed: -```fe +```fe ignore let x = 5 x = 10 // Error: cannot assign to immutable variable ``` @@ -70,7 +70,7 @@ This design choice promotes: When you need a variable whose value can change, use the `mut` keyword: -```fe +```fe ignore let mut counter = 0 counter = 1 // OK: counter is mutable counter = 2 // OK: can reassign multiple times @@ -82,7 +82,7 @@ The `mut` keyword signals to readers that this variable's value will change duri Use `mut` when you genuinely need to modify a value: -```fe +```fe ignore let mut total = 0 for item in items { total = total + item.value @@ -95,7 +95,7 @@ Prefer immutable variables when possible. If you find yourself using `mut` frequ Fe allows you to declare a new variable with the same name as a previous one. The new declaration *shadows* the previous one: -```fe +```fe ignore let x = 5 let x = x + 1 // x is now 6, shadows the previous x let x = x * 2 // x is now 12, shadows again @@ -106,7 +106,7 @@ Shadowing is different from mutation: - **Shadowing** creates a new variable (can even change the type) - **Mutation** modifies an existing variable's value -```fe +```fe ignore // Shadowing allows type changes let value = "42" // value is a String let value: u256 = 42 // value is now u256 (new variable) @@ -125,7 +125,7 @@ Shadowing is useful when: - Converting between types while keeping a meaningful name - Reusing a name in a new scope without `mut` -```fe +```fe ignore // Transform through steps let input = get_raw_input() let input = validate(input) @@ -136,7 +136,7 @@ let input = process(input) Variables are scoped to the block in which they're declared. A block is code enclosed in curly braces `{}`: -```fe +```fe ignore let outer = 1 { @@ -152,7 +152,7 @@ let outer = 1 Inner scopes can access variables from outer scopes: -```fe +```fe ignore let x = 10 { @@ -165,7 +165,7 @@ let x = 10 You can shadow an outer variable within an inner scope: -```fe +```fe ignore let x = 5 { @@ -182,20 +182,20 @@ The `let` statement supports pattern matching, allowing you to destructure value ### Tuple Destructuring -```fe +```fe ignore let point = (10, 20) let (x, y) = point // x = 10, y = 20 ``` You can ignore parts of a tuple with `_`: -```fe +```fe ignore let (x, _) = point // Only bind x, ignore y ``` ### Struct Destructuring -```fe +```fe ignore struct Point { x: u256 y: u256 @@ -207,7 +207,7 @@ let Point { x, y } = point // x = 10, y = 20 You can also rename bindings: -```fe +```fe ignore let Point { x: horizontal, y: vertical } = point // horizontal = 10, vertical = 20 ``` @@ -216,7 +216,7 @@ let Point { x: horizontal, y: vertical } = point Use `mut` in patterns to make specific bindings mutable: -```fe +```fe ignore let (mut x, y) = (1, 2) x = 10 // OK: x is mutable // y = 20 // Error: y is immutable @@ -224,7 +224,7 @@ x = 10 // OK: x is mutable In struct patterns: -```fe +```fe ignore let Point { mut x, y } = point x = 100 // OK: x is mutable ``` diff --git a/src/content/docs/ingots/dependencies.md b/src/content/docs/ingots/dependencies.md index a74a5f5..5489b22 100644 --- a/src/content/docs/ingots/dependencies.md +++ b/src/content/docs/ingots/dependencies.md @@ -110,7 +110,7 @@ contracts = { source = "https://github.com/org/monorepo.git", rev = "e7f8a9b0c1d Once declared, import from dependencies using their name: -```fe +```fe ignore // Import from the 'utils' dependency use utils::calculate_fee use utils::SafeMath @@ -132,7 +132,7 @@ The name you give a dependency determines its import path: helpers = "../some/path/to/utils" ``` -```fe +```fe ignore // Import using the name 'helpers', not the directory name use helpers::some_function ``` diff --git a/src/content/docs/ingots/project-structure.md b/src/content/docs/ingots/project-structure.md index 23c5a3b..dee4311 100644 --- a/src/content/docs/ingots/project-structure.md +++ b/src/content/docs/ingots/project-structure.md @@ -76,7 +76,7 @@ All Fe source files live in the `src/` directory. The compiler discovers files m Every ingot must have a `src/lib.fe` file. This is the entrypoint that defines what the ingot exports: -```fe +```fe ignore // src/lib.fe // Re-export items from other modules @@ -93,7 +93,7 @@ pub struct Config { Split code across multiple files for better organization: -```fe +```fe ignore // src/lib.fe pub use token::Token pub use utils::calculate_fee @@ -122,7 +122,7 @@ src/ └── approval.fe ``` -```fe +```fe ignore // src/messages/mod.fe pub use transfer::Transfer pub use approval::Approval diff --git a/src/content/docs/ingots/publishing.md b/src/content/docs/ingots/publishing.md index e43109d..197976a 100644 --- a/src/content/docs/ingots/publishing.md +++ b/src/content/docs/ingots/publishing.md @@ -26,7 +26,7 @@ Use semantic versioning: Export only what users need in `src/lib.fe`: -```fe +```fe ignore // Export public items pub use token::Token pub use token::Transfer @@ -40,7 +40,7 @@ use internal::validate_amount Add comments explaining public items: -```fe +```fe ignore /// A standard ERC20 token implementation. /// /// Supports transfer, approve, and transferFrom operations. diff --git a/src/content/docs/messages/defining-messages.md b/src/content/docs/messages/defining-messages.md index 772828b..db70803 100644 --- a/src/content/docs/messages/defining-messages.md +++ b/src/content/docs/messages/defining-messages.md @@ -9,7 +9,7 @@ Messages define the external interface of a contract—the operations that can b Define a message group using the `msg` keyword: -```fe +```fe ignore msg TokenMsg { #[selector = 0xa9059cbb] Transfer { to: u256, amount: u256 } -> bool, @@ -28,7 +28,7 @@ Each message group contains one or more **variants**—individual operations tha A variant defines a single callable operation: -```fe +```fe ignore #[selector = 0xa9059cbb] Transfer { to: u256, amount: u256 } -> bool ``` @@ -43,7 +43,7 @@ Components: Some operations don't need parameters: -```fe +```fe ignore #[selector = 0x18160ddd] TotalSupply -> u256 ``` @@ -52,7 +52,7 @@ TotalSupply -> u256 Operations that don't return a value omit the return type: -```fe +```fe ignore #[selector = 0x42842e0e] SafeTransfer { from: u256, to: u256, token_id: u256 } ``` @@ -63,7 +63,7 @@ This implicitly returns `()` (unit). A simple token message interface: -```fe +```fe ignore msg Erc20 { #[selector = 0xa9059cbb] Transfer { to: u256, amount: u256 } -> bool, @@ -98,7 +98,7 @@ Messages provide: Messages are handled in recv blocks within contracts: -```fe +```fe ignore contract Token { // storage fields... diff --git a/src/content/docs/messages/fields.md b/src/content/docs/messages/fields.md index 04468d5..3ec1eb4 100644 --- a/src/content/docs/messages/fields.md +++ b/src/content/docs/messages/fields.md @@ -9,7 +9,7 @@ Message fields define the parameters that callers pass when invoking a message v Fields are defined inside curly braces, similar to struct fields: -```fe +```fe ignore Transfer { to: u256, amount: u256 } -> bool ``` @@ -25,7 +25,7 @@ Message fields support all Fe types: ### Primitive Types -```fe +```fe ignore msg Example { #[selector = 0x00000001] WithPrimitives { @@ -38,7 +38,7 @@ msg Example { ### Compound Types -```fe +```fe ignore msg Example { #[selector = 0x00000002] WithTuple { coords: (u256, u256) } -> bool, @@ -52,7 +52,7 @@ msg Example { Variants can have no fields: -```fe +```fe ignore msg Query { #[selector = 0x18160ddd] TotalSupply -> u256, @@ -66,7 +66,7 @@ msg Query { Field order is significant for ABI encoding. The order in which fields are defined determines how calldata is decoded: -```fe +```fe ignore // These are different! Transfer { to: u256, amount: u256 } Transfer { amount: u256, to: u256 } @@ -78,7 +78,7 @@ When implementing standard interfaces like ERC20, ensure field order matches the In recv blocks, destructure fields to access their values: -```fe +```fe ignore recv TokenMsg { Transfer { to, amount } -> bool { // 'to' and 'amount' are available here @@ -89,7 +89,7 @@ recv TokenMsg { You can also rename fields during destructuring: -```fe +```fe ignore recv TokenMsg { Transfer { to: recipient, amount: value } -> bool { // Use 'recipient' and 'value' instead @@ -102,7 +102,7 @@ recv TokenMsg { Use `_` to ignore fields you don't need: -```fe +```fe ignore recv TokenMsg { Transfer { to, amount: _ } -> bool { // Only use 'to', ignore amount @@ -113,7 +113,7 @@ recv TokenMsg { Use `..` to ignore remaining fields: -```fe +```fe ignore recv TokenMsg { TransferFrom { from, .. } -> bool { // Only use 'from', ignore to and amount diff --git a/src/content/docs/messages/handler-syntax.md b/src/content/docs/messages/handler-syntax.md index 473cdca..7cc46c3 100644 --- a/src/content/docs/messages/handler-syntax.md +++ b/src/content/docs/messages/handler-syntax.md @@ -9,7 +9,7 @@ Handlers are the functions inside recv blocks that process incoming messages. Ea A handler consists of a pattern, optional return type, and body: -```fe +```fe ignore VariantName { fields } -> ReturnType { // handler body } @@ -17,7 +17,7 @@ VariantName { fields } -> ReturnType { For handlers that don't return a value: -```fe +```fe ignore VariantName { fields } { // handler body, implicitly returns () } @@ -31,7 +31,7 @@ Handlers use pattern matching to destructure message fields: Extract fields by their names: -```fe +```fe ignore recv TokenMsg { Transfer { to, amount } -> bool { // 'to' and 'amount' are available as local variables @@ -44,7 +44,7 @@ recv TokenMsg { Give fields different local names: -```fe +```fe ignore recv TokenMsg { Transfer { to: recipient, amount: value } -> bool { // Use 'recipient' and 'value' instead of 'to' and 'amount' @@ -57,7 +57,7 @@ recv TokenMsg { Use `_` to ignore specific fields: -```fe +```fe ignore recv TokenMsg { Transfer { to, amount: _ } -> bool { // Only use 'to', ignore the amount @@ -68,7 +68,7 @@ recv TokenMsg { Use `..` to ignore all remaining fields: -```fe +```fe ignore recv TokenMsg { TransferFrom { from, .. } -> bool { // Only use 'from', ignore 'to' and 'amount' @@ -81,7 +81,7 @@ recv TokenMsg { For variants without parameters, omit the braces: -```fe +```fe ignore recv TokenMsg { TotalSupply -> u256 { get_total_supply() @@ -95,7 +95,7 @@ recv TokenMsg { The return type must match the message variant's declaration: -```fe +```fe ignore msg Query { #[selector = 0x70a08231] BalanceOf { account: u256 } -> u256, @@ -113,7 +113,7 @@ recv Query { Handlers without a return type implicitly return `()`: -```fe +```fe ignore msg Commands { #[selector = 0x42842e0e] SafeTransfer { from: u256, to: u256, token_id: u256 }, @@ -133,7 +133,7 @@ Handler bodies contain the implementation logic. They can use all standard Fe ex ### Simple Handlers -```fe +```fe ignore recv TokenMsg { TotalSupply -> u256 { 1000000 @@ -153,7 +153,7 @@ recv TokenMsg { Use `return` for early exits: -```fe +```fe ignore recv TokenMsg { Transfer { to, amount } -> bool { if amount == 0 { @@ -171,7 +171,7 @@ recv TokenMsg { Handlers typically delegate to helper functions: -```fe +```fe ignore fn validate_transfer(to: u256, amount: u256) -> bool { to != 0 && amount > 0 } @@ -197,7 +197,7 @@ recv TokenMsg { Handlers access contract state through effects: -```fe +```fe ignore pub struct TokenStorage { pub balances: StorageMap, pub total_supply: u256, @@ -236,7 +236,7 @@ contract Token { Handlers can access transaction context using built-in functions: -```fe +```fe ignore recv TokenMsg { Transfer { to, amount } -> bool { let sender = caller() // Get the message sender diff --git a/src/content/docs/messages/interfaces.md b/src/content/docs/messages/interfaces.md index 0f3b85c..1dc9f85 100644 --- a/src/content/docs/messages/interfaces.md +++ b/src/content/docs/messages/interfaces.md @@ -9,7 +9,7 @@ Message groups in Fe serve as interface definitions. They specify what operation A message group defines a contract interface: -```fe +```fe ignore msg Erc20 { #[selector = 0xa9059cbb] Transfer { to: u256, amount: u256 } -> bool, @@ -37,7 +37,7 @@ Any contract with `recv Erc20 { ... }` implements this interface. Under the hood, each message variant becomes a struct that implements the `MsgVariant` trait: -```fe +```fe ignore // Conceptually, Transfer becomes: struct Transfer { to: u256, @@ -58,7 +58,7 @@ The `MsgVariant` trait provides: When you write: -```fe +```fe ignore msg TokenMsg { #[selector = 0xa9059cbb] Transfer { to: u256, amount: u256 } -> bool, @@ -67,7 +67,7 @@ msg TokenMsg { The compiler generates equivalent code like: -```fe +```fe ignore // The variant as a struct struct TokenMsg_Transfer { to: u256, @@ -90,7 +90,7 @@ This desugaring enables: Define standard interfaces as separate message groups: -```fe +```fe ignore // Core ERC20 operations msg Erc20 { #[selector = 0xa9059cbb] @@ -125,7 +125,7 @@ msg Erc20Permit { Contracts can implement any combination: -```fe +```fe ignore // Basic token contract SimpleToken { recv Erc20 { /* ... */ } @@ -149,7 +149,7 @@ contract FullToken { Create your own interfaces for custom protocols: -```fe +```fe ignore msg Ownable { #[selector = 0x8da5cb5b] Owner -> u256, @@ -183,7 +183,7 @@ contract ManagedToken { Document your interfaces with comments: -```fe +```fe ignore /// Standard ERC20 token interface /// /// Defines the core operations for fungible tokens: diff --git a/src/content/docs/messages/multiple-types.md b/src/content/docs/messages/multiple-types.md index 30282b6..593f8d7 100644 --- a/src/content/docs/messages/multiple-types.md +++ b/src/content/docs/messages/multiple-types.md @@ -9,7 +9,7 @@ Contracts often need to implement multiple interfaces—for example, an NFT mark A contract can have multiple recv blocks, each handling a different message type: -```fe +```fe ignore msg Erc20 { #[selector = 0xa9059cbb] Transfer { to: u256, amount: u256 } -> bool, @@ -75,7 +75,7 @@ contract Token { Selectors must be unique across **all** recv blocks in a contract. The compiler will error if two variants share a selector: -```fe +```fe ignore msg InterfaceA { #[selector = 0x12345678] Operation { } -> bool, @@ -104,7 +104,7 @@ This ensures the contract can unambiguously route incoming calls. Multiple recv blocks can share the same contract state: -```fe +```fe ignore pub struct TokenStorage { pub balances: StorageMap, pub allowances: StorageMap>, @@ -146,7 +146,7 @@ contract Token { You can mix named recv blocks with a bare recv block: -```fe +```fe ignore contract Hybrid { // Named block: implements full ERC20 interface recv Erc20 { @@ -174,7 +174,7 @@ contract Hybrid { ### ERC721 + Metadata + Enumerable -```fe +```fe ignore contract NFT { recv Erc721 { OwnerOf { token_id } -> u256 { /* ... */ } @@ -203,7 +203,7 @@ contract NFT { ### Token + Governance -```fe +```fe ignore contract GovernanceToken { recv Erc20 { // Standard token operations @@ -225,7 +225,7 @@ contract GovernanceToken { For contracts with many message types, consider organizing handlers logically: -```fe +```fe ignore // Group related helper functions fn transfer_tokens(from: u256, to: u256, amount: u256) uses mut TokenStorage -> bool { // ... diff --git a/src/content/docs/messages/receive-blocks.md b/src/content/docs/messages/receive-blocks.md index b5c6e39..94aaeab 100644 --- a/src/content/docs/messages/receive-blocks.md +++ b/src/content/docs/messages/receive-blocks.md @@ -9,7 +9,7 @@ Receive blocks (`recv`) are where contracts handle incoming messages. They conne A named recv block handles all variants of a specific message type: -```fe +```fe ignore msg TokenMsg { #[selector = 0xa9059cbb] Transfer { to: u256, amount: u256 } -> bool, @@ -37,7 +37,7 @@ contract Token { Named recv blocks must handle **all** variants of the message type: -```fe +```fe ignore contract Token { recv TokenMsg { Transfer { to, amount } -> bool { @@ -54,7 +54,7 @@ The compiler ensures you don't forget any handlers. A bare recv block handles messages without specifying a message type: -```fe +```fe ignore contract Example { recv { TokenMsg::Transfer { to, amount } -> bool { @@ -77,7 +77,7 @@ In bare blocks: Recv blocks appear inside contract definitions after fields and the init block: -```fe +```fe ignore contract Token { // Fields total_supply: u256, @@ -101,7 +101,7 @@ contract Token { Each handler in a recv block has: -```fe +```fe ignore VariantName { fields } -> ReturnType { // handler body } @@ -115,7 +115,7 @@ VariantName { fields } -> ReturnType { Handlers can use effects to access contract state: -```fe +```fe ignore pub struct TokenStorage { pub balances: StorageMap, } @@ -150,7 +150,7 @@ contract Token { A contract can have multiple recv blocks for different message types: -```fe +```fe ignore contract MultiInterface { recv Erc20 { Transfer { to, amount } -> bool { /* ... */ } diff --git a/src/content/docs/messages/selectors.md b/src/content/docs/messages/selectors.md index 0179c69..bffc223 100644 --- a/src/content/docs/messages/selectors.md +++ b/src/content/docs/messages/selectors.md @@ -21,7 +21,7 @@ Fe uses the same selector mechanism as Solidity, ensuring contracts can interope Specify selectors using the `#[selector]` attribute: -```fe +```fe ignore msg TokenMsg { #[selector = 0xa9059cbb] Transfer { to: u256, amount: u256 } -> bool, @@ -35,7 +35,7 @@ msg TokenMsg { Selectors are specified as hexadecimal values: -```fe +```fe ignore #[selector = 0xa9059cbb] // Hex format (preferred) #[selector = 2835717307] // Decimal format (also valid) ``` @@ -46,7 +46,7 @@ The value must fit in 4 bytes (u32). Every message variant must have a selector: -```fe +```fe ignore msg Example { // Error: missing selector NoSelector { value: u256 } -> bool, @@ -61,7 +61,7 @@ msg Example { Selectors must be unique across all recv blocks in a contract: -```fe +```fe ignore msg MsgA { #[selector = 0x12345678] Operation { } -> bool, @@ -131,7 +131,7 @@ cast sig "transfer(address,uint256)" :::note[Future: Compile-Time Selector Computation] Fe will provide a `sol_sig` const function for computing selectors at compile time: -```fe +```fe ignore msg TokenMsg { #[selector = sol_sig("balanceOf(address)")] BalanceOf { account: u256 } -> u256, @@ -145,7 +145,7 @@ This will allow you to define selectors using the Solidity signature string dire For non-standard interfaces, you can use any unique 4-byte value: -```fe +```fe ignore msg CustomProtocol { #[selector = 0x00000001] Initialize { config: u256 }, @@ -172,7 +172,7 @@ Fe requires explicit selectors rather than auto-generating them because: For example, you can name your variant `BalanceOf` or `balance_of`—it doesn't matter because the selector `0x70a08231` (derived from `balanceOf(address)`) is what the EVM uses for routing: -```fe +```fe ignore msg Erc20 { // Fe-style naming, but ABI-compatible with ERC20's balanceOf(address) #[selector = 0x70a08231] diff --git a/src/content/docs/structs/associated-functions.md b/src/content/docs/structs/associated-functions.md index c0aba7c..84edf45 100644 --- a/src/content/docs/structs/associated-functions.md +++ b/src/content/docs/structs/associated-functions.md @@ -9,7 +9,7 @@ Associated functions are functions defined in an impl block that don't take `sel Define an associated function by omitting `self`: -```fe +```fe ignore struct Point { x: u256, y: u256, @@ -32,13 +32,13 @@ impl Point { Use `::` syntax to call associated functions on the type: -```fe +```fe ignore let p = Point::origin() // Creates Point { x: 0, y: 0 } ``` Compare with method calls that use `.`: -```fe +```fe ignore let p = Point::origin() // Associated function: Type::function() let d = p.distance_from_origin() // Method: instance.method() ``` @@ -47,7 +47,7 @@ let d = p.distance_from_origin() // Method: instance.method() The most common associated function is `new()`, a constructor: -```fe +```fe ignore struct Counter { value: u256, max: u256, @@ -64,7 +64,7 @@ let counter = Counter::new(100) ### Constructor Variations -```fe +```fe ignore struct Config { threshold: u256, enabled: bool, @@ -105,7 +105,7 @@ let c3 = Config::create(100, true, 0x123) Associated functions can create different configurations: -```fe +```fe ignore struct Rectangle { width: u256, height: u256, @@ -134,7 +134,7 @@ let unit = Rectangle::unit() Associated functions can provide utilities related to the type: -```fe +```fe ignore struct Percentage { value: u256, // Stored as basis points (0-10000) } @@ -173,7 +173,7 @@ let result = p.apply(1000) // 500 A typical struct has both associated functions and methods: -```fe +```fe ignore struct TokenAmount { value: u256, decimals: u8, @@ -229,7 +229,7 @@ impl TokenAmount { Associated functions follow the same visibility rules as methods: -```fe +```fe ignore impl Config { // Public - callable from outside pub fn new() -> Config { diff --git a/src/content/docs/structs/definition.md b/src/content/docs/structs/definition.md index 0454966..55598f1 100644 --- a/src/content/docs/structs/definition.md +++ b/src/content/docs/structs/definition.md @@ -9,7 +9,7 @@ Structs are custom data types that group related values together. They're one of Define a struct with the `struct` keyword: -```fe +```fe ignore struct Point { x: u256, y: u256, @@ -22,7 +22,7 @@ Each field has a name and a type, separated by a colon. By default, struct fields are private. Use `pub` to make them publicly accessible: -```fe +```fe ignore struct Token { pub name: String, // Public - accessible from outside pub symbol: String, // Public @@ -35,7 +35,7 @@ struct Token { Make the entire struct public with `pub struct`: -```fe +```fe ignore pub struct TokenInfo { pub name: String, pub total_supply: u256, @@ -48,13 +48,13 @@ A `pub struct` can be used by other modules, but its fields still need individua Create struct instances using struct literal syntax: -```fe +```fe ignore let point = Point { x: 10, y: 20 } ``` All fields must be provided: -```fe +```fe ignore struct Rectangle { width: u256, height: u256, @@ -70,7 +70,7 @@ let rect = Rectangle { Access fields with dot notation: -```fe +```fe ignore let p = Point { x: 10, y: 20 } let x_value = p.x // 10 let y_value = p.y // 20 @@ -80,7 +80,7 @@ let y_value = p.y // 20 Structs are immutable by default. To modify, use `mut`: -```fe +```fe ignore let mut p = Point { x: 10, y: 20 } p.x = 30 // Now p is { x: 30, y: 20 } ``` @@ -89,7 +89,7 @@ p.x = 30 // Now p is { x: 30, y: 20 } Structs can contain other structs: -```fe +```fe ignore struct Bounds { min: Point, max: Point, @@ -107,7 +107,7 @@ let min_x = bounds.min.x // 0 Structs can hold any Fe type: -```fe +```fe ignore struct GameState { score: u256, position: (u256, u256), // Tuple @@ -124,7 +124,7 @@ struct Registry { Extract fields with pattern matching: -```fe +```fe ignore let p = Point { x: 10, y: 20 } // Destructure into variables diff --git a/src/content/docs/structs/helper-structs.md b/src/content/docs/structs/helper-structs.md index 245b7ef..bd41f26 100644 --- a/src/content/docs/structs/helper-structs.md +++ b/src/content/docs/structs/helper-structs.md @@ -9,7 +9,7 @@ Helper structs are regular (non-storage) structs used to organize data and logic Group related values into a struct: -```fe +```fe ignore struct TransferParams { from: u256, to: u256, @@ -28,7 +28,7 @@ This is clearer than passing many individual parameters. Use structs to return multiple values: -```fe +```fe ignore struct BalanceInfo { balance: u256, frozen: bool, @@ -48,7 +48,7 @@ fn get_account_info(account: u256) uses TokenStorage -> BalanceInfo { Encapsulate configuration: -```fe +```fe ignore struct FeeConfig { base_fee: u256, percentage_fee: u256, // In basis points @@ -78,7 +78,7 @@ fn process_with_fee(amount: u256, config: FeeConfig) -> u256 { Create structs for validated data: -```fe +```fe ignore struct ValidatedAmount { value: u256, } @@ -106,7 +106,7 @@ fn safe_transfer(to: u256, amount: ValidatedAmount) uses mut TokenStorage -> boo Create structs for operation results: -```fe +```fe ignore struct TransferResult { success: bool, new_sender_balance: u256, @@ -146,7 +146,7 @@ fn transfer_with_result( Structs with methods for calculations: -```fe +```fe ignore struct Percentage { basis_points: u256, } @@ -178,7 +178,7 @@ let fee = fee_rate.apply(1000) // 30 Use helper structs for complex construction: -```fe +```fe ignore struct TokenConfig { name_hash: u256, symbol_hash: u256, @@ -231,7 +231,7 @@ let config = TokenConfig::new() Group related operations using impl blocks: -```fe +```fe ignore struct MathUtils { // Empty struct - just a namespace for functions } @@ -262,7 +262,7 @@ let clamped = MathUtils::clamp(value, 10, 100) Helper structs can work alongside effects: -```fe +```fe ignore struct TransferRequest { from: u256, to: u256, diff --git a/src/content/docs/structs/impl-blocks.md b/src/content/docs/structs/impl-blocks.md index 42e00bd..82770c4 100644 --- a/src/content/docs/structs/impl-blocks.md +++ b/src/content/docs/structs/impl-blocks.md @@ -9,7 +9,7 @@ Impl blocks let you define methods on structs. Methods are functions associated Define methods in an `impl` block: -```fe +```fe ignore struct Counter { value: u256, } @@ -29,7 +29,7 @@ impl Counter { Methods take `self` as their first parameter, giving access to the struct instance: -```fe +```fe ignore struct Point { x: u256, y: u256, @@ -49,7 +49,7 @@ let mag_sq = p.magnitude_squared() // 25 Use `self` for read-only access, `mut self` when you need to modify: -```fe +```fe ignore impl Counter { // Read-only: uses self fn get(self) -> u256 { @@ -70,7 +70,7 @@ impl Counter { The compiler enforces this—you can't modify through a non-mut `self`: -```fe +```fe ignore impl Counter { fn broken(self) { self.value = 10 // Error: cannot mutate through immutable self @@ -82,7 +82,7 @@ impl Counter { Call methods with dot notation: -```fe +```fe ignore let mut counter = Counter { value: 0 } let v = counter.get() // 0 @@ -96,7 +96,7 @@ let v3 = counter.get() // 100 Methods returning `self` enable chaining: -```fe +```fe ignore struct Builder { width: u256, height: u256, @@ -130,7 +130,7 @@ let b = Builder { width: 0, height: 0, depth: 0 } You can split methods across multiple impl blocks: -```fe +```fe ignore struct Token { balance: u256, frozen: bool, @@ -167,7 +167,7 @@ impl Token { Methods can take parameters beyond `self`: -```fe +```fe ignore struct Wallet { balance: u256, } @@ -200,7 +200,7 @@ impl Wallet { Methods can be public or private: -```fe +```fe ignore impl Counter { // Public method pub fn get(self) -> u256 { @@ -218,7 +218,7 @@ impl Counter { Only structs can have impl blocks. Contracts cannot: -```fe +```fe ignore // ✓ This works - struct with impl struct Helper { data: u256, @@ -242,7 +242,7 @@ impl Token { // Error: cannot implement methods on contracts For contracts, use standalone functions with effects instead: -```fe +```fe ignore fn get_balance(account: u256) uses TokenStorage -> u256 { TokenStorage.balances.get(account) } diff --git a/src/content/docs/structs/storage-structs.md b/src/content/docs/structs/storage-structs.md index 5751ed9..2bb647e 100644 --- a/src/content/docs/structs/storage-structs.md +++ b/src/content/docs/structs/storage-structs.md @@ -9,7 +9,7 @@ Storage structs are structs designed to hold persistent blockchain state. They s A storage struct contains fields that persist on-chain: -```fe +```fe ignore pub struct TokenStorage { pub balances: StorageMap, pub total_supply: u256, @@ -27,7 +27,7 @@ Key characteristics: Storage structs become effect types. Functions declare them in `uses`: -```fe +```fe ignore pub struct TokenStorage { pub balances: StorageMap, pub total_supply: u256, @@ -48,7 +48,7 @@ fn set_balance(account: u256, amount: u256) uses mut TokenStorage { Contracts hold storage structs as fields and provide them as effects: -```fe +```fe ignore contract Token { store: TokenStorage, @@ -76,7 +76,7 @@ The `with (TokenStorage = store)` binds the contract field to the effect type. For simple contracts, one storage struct is sufficient: -```fe +```fe ignore pub struct CounterStorage { pub value: u256, } @@ -94,7 +94,7 @@ fn increment() uses mut CounterStorage { For complex contracts, split storage by concern: -```fe +```fe ignore // Token balances pub struct BalanceStorage { pub balances: StorageMap, @@ -120,7 +120,7 @@ pub struct PauseStorage { Each becomes an independent effect: -```fe +```fe ignore fn transfer(from: u256, to: u256, amount: u256) uses mut BalanceStorage, PauseStorage -> bool { @@ -135,7 +135,7 @@ fn transfer(from: u256, to: u256, amount: u256) `StorageMap` is the primary collection type for storage: -```fe +```fe ignore pub struct Registry { // Simple mapping: key -> value pub entries: StorageMap, @@ -147,7 +147,7 @@ pub struct Registry { Access patterns: -```fe +```fe ignore fn get_entry(key: u256) uses Registry -> u256 { Registry.entries.get(key) } @@ -173,7 +173,7 @@ The current `StorageMap` is a temporary implementation that will be replaced wit Storage structs and their fields are typically public: -```fe +```fe ignore pub struct TokenStorage { pub balances: StorageMap, // Public for effect access pub total_supply: u256, @@ -196,7 +196,7 @@ The `pub` on fields allows `TokenStorage.balances` syntax in functions using the A full token with storage structs: -```fe +```fe ignore // Storage definitions pub struct TokenStorage { pub balances: StorageMap, diff --git a/src/content/docs/traits/bounds.md b/src/content/docs/traits/bounds.md index 69fe1fb..937af86 100644 --- a/src/content/docs/traits/bounds.md +++ b/src/content/docs/traits/bounds.md @@ -9,7 +9,7 @@ Trait bounds specify what capabilities a generic type must have. They let you wr Add a bound with `: Trait` after the type parameter: -```fe +```fe ignore fn process(value: T) -> u256 { value.hash() // Safe: T must implement Hashable } @@ -17,7 +17,7 @@ fn process(value: T) -> u256 { Without the bound, you couldn't call `hash()`: -```fe +```fe ignore fn process(value: T) -> u256 { value.hash() // Error: T might not have hash() } @@ -31,7 +31,7 @@ Bounds enable the compiler to: 2. **Catch errors early**: Fail at call site, not deep in implementation 3. **Document requirements**: Show what types are expected -```fe +```fe ignore trait Printable { fn to_string(self) -> String } @@ -54,7 +54,7 @@ log(Secret { data: 42 }) // Compile error Require multiple traits with `+`: -```fe +```fe ignore trait Hashable { fn hash(self) -> u256 } @@ -71,7 +71,7 @@ fn describe(value: T) -> String { The type must implement all specified traits: -```fe +```fe ignore struct Token { id: u256, } @@ -95,7 +95,7 @@ describe(Token { id: 1 }) // Works: Token implements both Each type parameter can have its own bounds: -```fe +```fe ignore fn combine(a: A, b: B) -> String { let hash = a.hash() b.to_string() @@ -111,7 +111,7 @@ fn transform(input: T, output: U) { Generic structs can have bounds: -```fe +```fe ignore struct Cache { item: T, hash: u256, @@ -129,7 +129,7 @@ impl Cache { Impl blocks can add their own bounds: -```fe +```fe ignore struct Wrapper { value: T, } @@ -166,7 +166,7 @@ This means: ### Comparable Types -```fe +```fe ignore trait Comparable { fn less_than(self, other: Self) -> bool fn equals(self, other: Self) -> bool @@ -188,7 +188,7 @@ fn find(items: Array, target: T) -> bool { ### Default Values -```fe +```fe ignore trait Default { fn default() -> Self } @@ -203,7 +203,7 @@ fn or_default(value: Option) -> T { ### Cloneable Types -```fe +```fe ignore trait Clone { fn clone(self) -> Self } @@ -217,7 +217,7 @@ fn duplicate(value: T) -> (T, T) { Bounds and effects work together: -```fe +```fe ignore trait Storable { fn key(self) -> u256 } @@ -236,7 +236,7 @@ fn load(key: u256) uses Storage -> T { When bounds aren't satisfied, you get clear errors: -```fe +```fe ignore trait Hashable { fn hash(self) -> u256 } diff --git a/src/content/docs/traits/definition.md b/src/content/docs/traits/definition.md index b9003e9..691a4ae 100644 --- a/src/content/docs/traits/definition.md +++ b/src/content/docs/traits/definition.md @@ -9,7 +9,7 @@ Traits define shared behavior that types can implement. They're Fe's way of expr Define a trait with the `trait` keyword: -```fe +```fe ignore trait Drawable { fn draw(self) } @@ -21,7 +21,7 @@ This defines a trait called `Drawable` with one required method `draw`. Traits declare methods that implementing types must provide: -```fe +```fe ignore trait Summary { fn summarize(self) -> String } @@ -45,7 +45,7 @@ Each method signature specifies: In trait definitions, `Self` refers to the implementing type: -```fe +```fe ignore trait Cloneable { fn clone(self) -> Self // Returns the same type that implements this trait } @@ -59,7 +59,7 @@ trait Addable { Traits can require multiple methods: -```fe +```fe ignore trait Token { fn balance_of(self, account: u256) -> u256 fn transfer(mut self, to: u256, amount: u256) -> bool @@ -77,7 +77,7 @@ trait Owned { Make traits public with `pub`: -```fe +```fe ignore pub trait Serializable { fn serialize(self) -> Array fn deserialize(data: Array) -> Self @@ -90,7 +90,7 @@ Public traits can be implemented by types in other modules. Trait methods can have various parameter types: -```fe +```fe ignore trait Calculator { // Only self fn value(self) -> u256 @@ -112,7 +112,7 @@ trait Calculator { Each trait should represent one capability: -```fe +```fe ignore // Good: focused traits trait Readable { fn read(self) -> u256 @@ -133,7 +133,7 @@ trait ReadWritable { Design traits that can be combined: -```fe +```fe ignore trait Identifiable { fn id(self) -> u256 } @@ -154,7 +154,7 @@ trait Timestamped { ### Defining Interfaces -```fe +```fe ignore trait ERC20 { fn total_supply(self) -> u256 fn balance_of(self, account: u256) -> u256 @@ -167,7 +167,7 @@ trait ERC20 { ### Enabling Polymorphism -```fe +```fe ignore trait Validator { fn is_valid(self) -> bool } @@ -180,7 +180,7 @@ fn process(item: T) -> bool { ### Standardizing Behavior -```fe +```fe ignore trait Hashable { fn hash(self) -> u256 } diff --git a/src/content/docs/traits/generics.md b/src/content/docs/traits/generics.md index f8dff1f..e456ef8 100644 --- a/src/content/docs/traits/generics.md +++ b/src/content/docs/traits/generics.md @@ -9,7 +9,7 @@ Generic functions work with multiple types using type parameters. Instead of wri Define a generic function with type parameters in angle brackets: -```fe +```fe ignore fn identity(value: T) -> T { value } @@ -17,7 +17,7 @@ fn identity(value: T) -> T { `T` is a type parameter—a placeholder for any concrete type. When called, the compiler substitutes the actual type: -```fe +```fe ignore let x = identity(42) // T is u256 let y = identity(true) // T is bool ``` @@ -26,7 +26,7 @@ let y = identity(true) // T is bool Functions can have multiple type parameters: -```fe +```fe ignore fn pair(first: A, second: B) -> (A, B) { (first, second) } @@ -38,7 +38,7 @@ let p = pair(1, true) // (u256, bool) Usually, you need to constrain what types are allowed. Use trait bounds: -```fe +```fe ignore trait Printable { fn to_string(self) -> String } @@ -50,7 +50,7 @@ fn print_value(value: T) -> String { Now `print_value` only accepts types that implement `Printable`: -```fe +```fe ignore struct Message { text: String, } @@ -69,7 +69,7 @@ print_value(msg) // Works: Message implements Printable Structs can also be generic: -```fe +```fe ignore struct Wrapper { value: T, } @@ -92,7 +92,7 @@ let v = w.get() // 42 Methods can introduce their own type parameters: -```fe +```fe ignore struct Container { item: T, } @@ -115,7 +115,7 @@ impl Container { Write once, use with many types: -```fe +```fe ignore // Without generics: separate functions for each type fn max_u256(a: u256, b: u256) -> u256 { if a > b { a } else { b } @@ -135,7 +135,7 @@ fn max(a: T, b: T) -> T { Generics preserve type information: -```fe +```fe ignore fn first(items: Array) -> T { items[0] } @@ -150,7 +150,7 @@ let n = first(numbers) // n is u256, not a generic "any" type Usually the compiler infers types: -```fe +```fe ignore let x = identity(42) // Compiler infers T = u256 ``` @@ -158,7 +158,7 @@ let x = identity(42) // Compiler infers T = u256 Sometimes you need to specify types explicitly: -```fe +```fe ignore let x = identity::(42) ``` @@ -166,7 +166,7 @@ let x = identity::(42) ### Swap Function -```fe +```fe ignore fn swap(a: T, b: T) -> (T, T) { (b, a) } @@ -176,7 +176,7 @@ let (x, y) = swap(1, 2) // (2, 1) ### Optional/Default Pattern -```fe +```fe ignore fn or_default(value: Option) -> T { match value { Option::Some(v) => v, @@ -187,7 +187,7 @@ fn or_default(value: Option) -> T { ### Transform Pattern -```fe +```fe ignore trait Transform { fn transform(self) -> Self } @@ -201,7 +201,7 @@ fn apply_twice(value: T) -> T { ### Single Bound -```fe +```fe ignore fn process(item: T) -> u256 { item.hash() } @@ -209,7 +209,7 @@ fn process(item: T) -> u256 { ### Multiple Bounds -```fe +```fe ignore fn process(item: T) -> String { let hash = item.hash() item.to_string() @@ -230,7 +230,7 @@ Generics and effects serve different purposes: They can be combined: -```fe +```fe ignore fn get_value(key: u256) uses Storage -> T { // Generic return type with storage effect Storage.get(key) diff --git a/src/content/docs/traits/implementing.md b/src/content/docs/traits/implementing.md index 24c420c..639b2cf 100644 --- a/src/content/docs/traits/implementing.md +++ b/src/content/docs/traits/implementing.md @@ -9,7 +9,7 @@ Once a trait is defined, types can implement it to provide the required behavior Implement a trait for a struct: -```fe +```fe ignore trait Greetable { fn greet(self) -> String } @@ -27,7 +27,7 @@ impl Greetable for Person { Now `Person` has the `greet` method: -```fe +```fe ignore let person = Person { name: "Alice" } let greeting = person.greet() // "Alice" ``` @@ -36,7 +36,7 @@ let greeting = person.greet() // "Alice" You must implement every method declared in the trait: -```fe +```fe ignore trait Shape { fn area(self) -> u256 fn perimeter(self) -> u256 @@ -60,7 +60,7 @@ impl Shape for Rectangle { Missing any method causes a compile error: -```fe +```fe ignore impl Shape for Rectangle { fn area(self) -> u256 { self.width * self.height @@ -73,7 +73,7 @@ impl Shape for Rectangle { Different types can implement the same trait: -```fe +```fe ignore trait Area { fn area(self) -> u256 } @@ -116,7 +116,7 @@ impl Area for Triangle { A type can implement multiple traits: -```fe +```fe ignore trait Printable { fn to_string(self) -> String } @@ -157,7 +157,7 @@ impl Comparable for Token { For methods with `mut self`, the implementation can modify the struct: -```fe +```fe ignore trait Counter { fn count(self) -> u256 fn increment(mut self) @@ -187,7 +187,7 @@ impl Counter for SimpleCounter { `Self` in the implementation refers to the concrete type: -```fe +```fe ignore trait Duplicatable { fn duplicate(self) -> Self } @@ -211,7 +211,7 @@ impl Duplicatable for Point { A type can have both trait methods and regular methods: -```fe +```fe ignore struct Wallet { balance: u256, } @@ -241,7 +241,7 @@ impl Printable for Wallet { Both are available on the type: -```fe +```fe ignore let mut wallet = Wallet::new(100) // Associated function wallet.deposit(50) // Regular method let s = wallet.to_string() // Trait method @@ -251,7 +251,7 @@ let s = wallet.to_string() // Trait method Trait implementations follow the trait's visibility: -```fe +```fe ignore // Public trait pub trait Serializable { fn serialize(self) -> u256 diff --git a/src/content/docs/traits/standard-traits.md b/src/content/docs/traits/standard-traits.md index e93ff1a..c871536 100644 --- a/src/content/docs/traits/standard-traits.md +++ b/src/content/docs/traits/standard-traits.md @@ -9,7 +9,7 @@ Fe provides several built-in traits that enable core language functionality. Und The `MsgVariant` trait is automatically implemented for message variants. It provides the selector and return type information needed for ABI compatibility. -```fe +```fe ignore trait MsgVariant { const SELECTOR: u32 type Return @@ -18,7 +18,7 @@ trait MsgVariant { When you define a message: -```fe +```fe ignore msg TokenMsg { #[selector = 0xa9059cbb] Transfer { to: u256, amount: u256 } -> bool, @@ -27,7 +27,7 @@ msg TokenMsg { The compiler generates an implementation like: -```fe +```fe ignore // Conceptually generated by the compiler impl MsgVariant for TokenMsg_Transfer { const SELECTOR: u32 = 0xa9059cbb @@ -46,7 +46,7 @@ Fe provides traits for numeric operations: ### Arithmetic -```fe +```fe ignore trait Add { fn add(self, other: Self) -> Self } @@ -68,7 +68,7 @@ Primitive numeric types implement these automatically. ### Comparison -```fe +```fe ignore trait Eq { fn eq(self, other: Self) -> bool } @@ -89,7 +89,7 @@ While Fe may not have all these as built-ins yet, these patterns are commonly de Provides a default value: -```fe +```fe ignore trait Default { fn default() -> Self } @@ -111,7 +111,7 @@ let c = Counter::default() Creates a copy of a value: -```fe +```fe ignore trait Clone { fn clone(self) -> Self } @@ -132,7 +132,7 @@ impl Clone for Point { Converts between types: -```fe +```fe ignore trait Into { fn into(self) -> T } @@ -158,7 +158,7 @@ For your projects, define common traits that types should implement: ### Identifiable -```fe +```fe ignore trait Identifiable { fn id(self) -> u256 } @@ -193,7 +193,7 @@ fn get_id(item: T) -> u256 { ### Validatable -```fe +```fe ignore trait Validatable { fn is_valid(self) -> bool } @@ -221,7 +221,7 @@ fn process(item: T) -> bool { ### Hashable -```fe +```fe ignore trait Hashable { fn hash(self) -> u256 } @@ -246,7 +246,7 @@ Define trait "interfaces" for your contract patterns: ### ERC-Style Traits -```fe +```fe ignore trait ERC20 { fn total_supply(self) -> u256 fn balance_of(self, account: u256) -> u256 @@ -274,7 +274,7 @@ trait Pausable { Build complex behaviors from simple traits: -```fe +```fe ignore trait Readable { fn read(self) -> u256 }