Indentation¶
Like Python, Sharpy uses indentation instead of curly braces to demarcate blocks.
Indentation Rules¶
- Exactly 4 spaces per indentation level (enforced)
- Tabs are not allowed for indentation
- Mixed spaces and tabs cause a lexical error
- Indentation must be consistent within a file
Lexer Edge Cases¶
The following rules govern how the lexer handles indentation in special cases:
| Scenario | Behavior |
|---|---|
| Blank lines | Do not affect indentation tracking. May have any amount of whitespace or none. |
| Comment-only lines | Do not require indentation matching. A # comment at column 0 inside an indented block is valid. |
| Trailing whitespace | Ignored. Does not affect indentation calculations. |
| Tab characters | Lexer error: "Tab characters are not permitted for indentation. Use 4 spaces per level." |
| Inconsistent indentation | Lexer error: "Indentation must be a multiple of 4 spaces. Found N spaces." |
def example():
x = 1 # 4 spaces - valid
# Comment at column 0 is OK
# Blank line above - OK
if x > 0:
y = 2 # 8 spaces - valid
z = 3 # Tab character - ERROR
w = 4 # 10 spaces - ERROR: not multiple of 4
Indentation Stack Behavior:
The lexer maintains a stack of indentation levels:
- Stack starts with
[0](column 0) - When indentation increases by exactly 4 spaces, push new level and emit
INDENT - When indentation decreases, pop levels and emit
DEDENTfor each popped level - At EOF, emit
DEDENTfor each level remaining on stack (except 0)
def foo(): # Stack: [0] → [0, 4], emit INDENT
if True: # Stack: [0, 4] → [0, 4, 8], emit INDENT
x = 1
y = 2 # Stack: [0, 4, 8] → [0, 4], emit DEDENT
z = 3 # Stack: [0, 4] → [0], emit DEDENT
# EOF # Stack: [0], nothing more to emit
Implementation
- 🔄 Lowered - The lexer tracks indentation levels via an indentation stack, emitting INDENT/DEDENT tokens. These are converted to C# braces { } during code generation.