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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions docs/features.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,28 @@ a = a + 1;
}
```

- **Loop Control (`heel` and `stay`)**
Control loop execution with break and continue:
```bash
# heel - break out of loop immediately
wagtail(i < 10) {
bark(i);
if i == 5 {
heel; # Exit loop when i equals 5
}
i = i + 1;
}

# stay - skip to next iteration
wagtail(i < 10) {
i = i + 1;
if i % 2 == 0 {
stay; # Skip even numbers
}
bark(i);
}
```

- **Conditionals (`sniff` / `else`)**
Branch execution based on conditions:
```bash
Expand Down Expand Up @@ -70,6 +92,7 @@ name = fetch("Enter your name: ");
- Currently supports only integer and string data types.
- No functions or user-defined procedures yet.
- Basic error reporting without detailed debug info.
- Loop control statements (`heel` and `stay`) must be used inside loops.

---

Expand Down
37 changes: 37 additions & 0 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,43 @@ This loops from 0 to 4, printing each value.

---

## Loop Control (`heel` and `stay`)

### Break Statement (`heel`)
Use `heel` to exit a loop immediately:
```bash
i = 0;
wagtail(i < 10) {
bark(i);
i = i + 1;
sniff i == 5 {
heel; # Exit loop when i equals 5
}
}
```
This will print 0, 1, 2, 3, 4, 5 and then exit the loop.

### Continue Statement (`stay`)
Use `stay` to skip to the next iteration of a loop:
```bash
i = 0;
wagtail(i < 10) {
i = i + 1;
sniff i % 2 == 0 {
stay; # Skip even numbers
}
bark(i);
}
```
This will print only odd numbers: 1, 3, 5, 7, 9.

### Important Notes
- `heel` and `stay` can only be used inside loops
- Using them outside a loop will cause an error
- They work with both `wagtail` loops and nested loops

---

## Conditional Statement (`sniff`)

Use `sniff` to check conditions and optionally `else` for alternate execution:
Expand Down
22 changes: 22 additions & 0 deletions doglang/SyntaxAnalyser.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@ def statement(self):
return self.loop_stmt()
elif token.value=='sniff':
return self.conditional_statement()
elif token.value=='heel':
return self.heel_stmt()
elif token.value=='stay':
return self.stay_stmt()

elif token.token_type == Tokens.IDENTIFIER:
# Look ahead to see if the next token is an assignment operator
Expand Down Expand Up @@ -162,3 +166,21 @@ def print_stmt(self):
self.match(Tokens.KEYWORD,'bark') #bark keyword
node.addchild(self.expressions())
return node

def heel_stmt(self):
"""Parse a heel (break) statement"""
node = AST("heel")
self.match(Tokens.KEYWORD, 'heel')
# heel statements should end with semicolon
if self.current_element() and self.current_element().value == ';':
self.match(Tokens.SEMICOLON, ';')
return node

def stay_stmt(self):
"""Parse a stay (continue) statement"""
node = AST("stay")
self.match(Tokens.KEYWORD, 'stay')
# stay statements should end with semicolon
if self.current_element() and self.current_element().value == ';':
self.match(Tokens.SEMICOLON, ';')
return node
5 changes: 4 additions & 1 deletion doglang/Tokenizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class Tokens:
COMMENT = 'COMMENT'


keywords = {'bark','wagtail','fetch','sniff','else'}
keywords = {'bark','wagtail','fetch','sniff','else','heel','stay'}


arithmetic_operators = {'+', '-', '*', '/', '%'}
Expand All @@ -50,6 +50,9 @@ def Tokenizer(code):

# Process the code line by line to get the line number
for line_number, line in enumerate(code.splitlines(), 1):
# Skip comment lines
if line.strip().startswith('#'):
continue
pattern = r'"(?:\\.|[^"\\])*"|[A-Za-z_]\w*|\d+|==|!=|>=|<=|&&|\|\||[+\-*/%]=?|[(){};,]|[<>]|='
tokenized_line = re.findall(pattern, line)

Expand Down
112 changes: 112 additions & 0 deletions doglang/loopcontrol.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
"""
Loop Control Module for DogLang

This module implements break and continue functionality for loops.
- 'heel' keyword acts as break (exits the loop immediately)
- 'stay' keyword acts as continue (skips to next iteration)

The module provides:
1. LoopControlError - Custom exception for invalid loop control usage
2. LoopControlHandler - Context manager for tracking loop control flow
3. Helper functions for validating and executing loop control statements
"""

from doglang.error import DogLangError

class LoopControlError(DogLangError):
"""Custom exception for loop control related errors"""
def __init__(self, error_type, message):
super().__init__(error_type, message)

class LoopControlHandler:
"""
Context manager to handle break and continue statements within loops.
Tracks the current loop context and handles control flow.
"""

def __init__(self):
self.should_break = False
self.should_continue = False
self.in_loop = False

def __enter__(self):
self.in_loop = True
return self

def __exit__(self, exc_type, exc_val, exc_tb):
self.in_loop = False
# Reset flags after exiting loop context
self.should_break = False
self.should_continue = False

def break_loop(self):
"""Signal that the current loop should be broken"""
if not self.in_loop:
raise LoopControlError("Syntax", "heel statement must be inside a loop")
self.should_break = True

def continue_loop(self):
"""Signal that the current loop should continue to next iteration"""
if not self.in_loop:
raise LoopControlError("Syntax", "stay statement must be inside a loop")
self.should_continue = True

def should_exit_loop(self):
"""Check if loop should be exited (break)"""
return self.should_break

def should_skip_iteration(self):
"""Check if current iteration should be skipped (continue)"""
return self.should_continue

def reset_flags(self):
"""Reset break and continue flags for next iteration"""
self.should_break = False
self.should_continue = False

# Global loop control handler instance
loop_control_handler = LoopControlHandler()

def execute_heel_statement():
"""
Execute a 'heel' (break) statement.
Raises LoopControlError if not used within a loop.
"""
loop_control_handler.break_loop()

def execute_stay_statement():
"""
Execute a 'stay' (continue) statement.
Raises LoopControlError if not used within a loop.
"""
loop_control_handler.continue_loop()

def should_break_loop():
"""Check if the current loop should be broken"""
return loop_control_handler.should_exit_loop()

def should_continue_loop():
"""Check if the current iteration should be skipped"""
return loop_control_handler.should_skip_iteration()

def reset_loop_control():
"""Reset loop control flags for next iteration"""
loop_control_handler.reset_flags()

def enter_loop_context():
"""Enter a new loop context"""
loop_control_handler.__enter__()

def exit_loop_context():
"""Exit the current loop context"""
loop_control_handler.__exit__(None, None, None)

def validate_loop_control_context():
"""
Validate that loop control statements are used in proper context.
This should be called during semantic analysis.
"""
# This will be used by the semantic analyzer to ensure
# heel and stay are only used within loops
pass

66 changes: 55 additions & 11 deletions doglang/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from doglang.Tokenizer import Tokenizer
from doglang.SemanticAnalyser import SemanticAnalyser
from doglang.error import DogLangError
from doglang.loopcontrol import execute_heel_statement, execute_stay_statement, should_break_loop, should_continue_loop, reset_loop_control, enter_loop_context, exit_loop_context

class Interpreter:
def __init__(self,code):
Expand All @@ -26,6 +27,10 @@ def visit(self,ast):
self.loop_stmt(ast.children)
elif ast.type == "conditional":
self.conditions(ast.children)
elif ast.type == "heel":
self.heel_stmt()
elif ast.type == "stay":
self.stay_stmt()

def assignment(self,children):
name = children[0].value
Expand Down Expand Up @@ -64,20 +69,59 @@ def print_stmt(self,children):


def loop_stmt(self,children):
# First, evaluate the condition
condition_node = next((child for child in children if child.type == "expression"), None)
body_nodes = [child for child in children if child.type != "expression"]
# Enter loop context
enter_loop_context()

if condition_node:
condition = self.expression_stmt(condition_node.children)
if condition:
# Execute the body of the loop
for node in body_nodes:
self.visit(node)
# Then check the condition again (recursively)
self.loop_stmt(children)
try:
# Get condition and body nodes
condition_node = next((child for child in children if child.type == "expression"), None)
body_nodes = [child for child in children if child.type != "expression"]

if condition_node:
# Main loop - keep running while condition is true
while True:
# Check condition at start of each iteration
condition = self.expression_stmt(condition_node.children)
if not condition:
break

# Execute the body of the loop
for node in body_nodes:
# Check for break before executing each statement
if should_break_loop():
return # Exit the entire loop

# Check for continue before executing each statement
if should_continue_loop():
reset_loop_control()
break # Skip to next iteration

self.visit(node)

# Check for break after executing each statement
if should_break_loop():
return # Exit the entire loop

# Check for continue after executing each statement
if should_continue_loop():
reset_loop_control()
break # Skip to next iteration

# Reset loop control flags for next iteration
reset_loop_control()
finally:
# Always exit loop context
exit_loop_context()


def heel_stmt(self):
"""Execute a heel (break) statement"""
execute_heel_statement()

def stay_stmt(self):
"""Execute a stay (continue) statement"""
execute_stay_statement()

def expression_stmt(self,children):
expression=""
for child in children:
Expand Down
37 changes: 37 additions & 0 deletions examples/loop_control_basic.doggy
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Basic Loop Control Examples
# This file demonstrates heel (break) and stay (continue) statements

# Example 1: Using heel to break out of a loop early
bark "Example 1: Breaking out of loop at 3";
i = 0;
wagtail i < 5 {
bark i;
i = i + 1;
sniff i == 3 {
heel;
}
}

# Example 2: Using stay to skip iterations
bark "Example 2: Skipping even numbers";
i = 0;
wagtail i < 10 {
i = i + 1;
sniff i % 2 == 0 {
stay;
}
bark i;
}

# Example 3: Breaking when a condition is met
bark "Example 3: Breaking when sum exceeds 10";
sum = 0;
i = 1;
wagtail i <= 10 {
sum = sum + i;
bark sum;
sniff sum == 10 {
heel;
}
i = i + 1;
}
11 changes: 11 additions & 0 deletions examples/simple_heel_test.doggy
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Simple heel test
bark "Testing heel (break)";
i = 0;
wagtail i < 5 {
bark i;
i = i + 1;
sniff i == 3 {
heel;
}
}
bark "Loop finished";
11 changes: 11 additions & 0 deletions examples/simple_stay_test.doggy
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Simple stay test
bark "Testing stay (continue)";
i = 0;
wagtail i < 5 {
i = i + 1;
sniff i == 3 {
stay;
}
bark i;
}
bark "Loop finished";
Loading
Loading