latest pushes
This commit is contained in:
parent
8f087db1b8
commit
14e5a2043d
@ -21,7 +21,7 @@
|
|||||||
|
|
||||||
# Usage of Bellande Rust Executable Builder
|
# Usage of Bellande Rust Executable Builder
|
||||||
- https://github.com/Architecture-Mechanism/bellande_rust_executable
|
- https://github.com/Architecture-Mechanism/bellande_rust_executable
|
||||||
- ```bellande_rust_executable -d dependencies.txt -s src -m bellos.rs -o executable/bellos```
|
- ```bellande_rust_executable -d dependencies.bellande -s src -m bellos.rs -o executable/bellos```
|
||||||
|
|
||||||
# Usage of Bellande Rust Importer
|
# Usage of Bellande Rust Importer
|
||||||
- https://github.com/Architecture-Mechanism/bellande_importer
|
- https://github.com/Architecture-Mechanism/bellande_importer
|
||||||
@ -37,7 +37,6 @@
|
|||||||
```
|
```
|
||||||
|
|
||||||
## Built-in Commands
|
## Built-in Commands
|
||||||
|
|
||||||
### Basic Commands
|
### Basic Commands
|
||||||
- **echo [args...]**: Print arguments to standard output.
|
- **echo [args...]**: Print arguments to standard output.
|
||||||
- **cd [directory]**: Change the current working directory.
|
- **cd [directory]**: Change the current working directory.
|
||||||
|
0
bellos_scripts/basic_math.bellos
Normal file → Executable file
0
bellos_scripts/basic_math.bellos
Normal file → Executable file
@ -1,54 +1,52 @@
|
|||||||
#!/usr/bin/env bellos
|
#!/usr/bin/env bellos
|
||||||
# File: control_structures.bellos
|
# File: control_structures.bellos
|
||||||
|
|
||||||
# Demonstrating if statements and loops
|
# Demonstrating if-else statements
|
||||||
|
echo "Demonstrating if-else statements:"
|
||||||
# If-else statement
|
|
||||||
echo "If-else statement:"
|
|
||||||
x=10
|
x=10
|
||||||
if [ $x -gt 5 ]; then
|
If [ $x -gt 5 ] Then
|
||||||
echo "x is greater than 5"
|
echo "x is greater than 5"
|
||||||
else
|
Else
|
||||||
echo "x is not greater than 5"
|
echo "x is not greater than 5"
|
||||||
fi
|
Fi
|
||||||
|
|
||||||
# Nested if-else
|
# Demonstrating nested if-else
|
||||||
echo "\nNested if-else:"
|
echo "Demonstrating nested if-else:"
|
||||||
y=20
|
y=20
|
||||||
if [ $x -gt 5 ]; then
|
If [ $x -gt 5 ] Then
|
||||||
if [ $y -gt 15 ]; then
|
If [ $y -gt 15 ] Then
|
||||||
echo "x is greater than 5 and y is greater than 15"
|
echo "x is greater than 5 and y is greater than 15"
|
||||||
else
|
Else
|
||||||
echo "x is greater than 5 but y is not greater than 15"
|
echo "x is greater than 5 but y is not greater than 15"
|
||||||
fi
|
Fi
|
||||||
else
|
Else
|
||||||
echo "x is not greater than 5"
|
echo "x is not greater than 5"
|
||||||
fi
|
Fi
|
||||||
|
|
||||||
# While loop
|
# Demonstrating while loop
|
||||||
echo "\nWhile loop:"
|
echo "Demonstrating while loop:"
|
||||||
counter=0
|
counter=0
|
||||||
while [ $counter -lt 5 ]; do
|
While [ $counter -lt 5 ] Do
|
||||||
echo "Counter: $counter"
|
echo "Counter: $counter"
|
||||||
counter=$((counter + 1))
|
counter=$((counter + 1))
|
||||||
done
|
Done
|
||||||
|
|
||||||
# For loop
|
# Demonstrating for loop
|
||||||
echo "\nFor loop:"
|
echo "Demonstrating for loop:"
|
||||||
for i in 1 2 3 4 5; do
|
For i In 1 2 3 4 5 Do
|
||||||
echo "Iteration: $i"
|
echo "Iteration: $i"
|
||||||
done
|
Done
|
||||||
|
|
||||||
# For loop with range
|
# Demonstrating for loop with command substitution
|
||||||
echo "\nFor loop with range:"
|
echo "Demonstrating for loop with command substitution:"
|
||||||
for i in $(seq 1 5); do
|
For i In $(seq 1 5) Do
|
||||||
echo "Number: $i"
|
echo "Number: $i"
|
||||||
done
|
Done
|
||||||
|
|
||||||
# Case statement
|
# Demonstrating case statement
|
||||||
echo "\nCase statement:"
|
echo "Demonstrating case statement:"
|
||||||
fruit="apple"
|
fruit="apple"
|
||||||
case $fruit in
|
Case $fruit In
|
||||||
"apple")
|
"apple")
|
||||||
echo "It's an apple"
|
echo "It's an apple"
|
||||||
;;
|
;;
|
||||||
@ -61,4 +59,14 @@ case $fruit in
|
|||||||
*)
|
*)
|
||||||
echo "Unknown fruit"
|
echo "Unknown fruit"
|
||||||
;;
|
;;
|
||||||
esac
|
Esac
|
||||||
|
|
||||||
|
# Demonstrating function
|
||||||
|
echo "Demonstrating function:"
|
||||||
|
Function greet (
|
||||||
|
echo "Hello, $1!"
|
||||||
|
)
|
||||||
|
greet "World"
|
||||||
|
greet "Bellos"
|
||||||
|
|
||||||
|
echo "Control structures demonstration completed."
|
||||||
|
0
bellos_scripts/file_operations.bellos
Normal file → Executable file
0
bellos_scripts/file_operations.bellos
Normal file → Executable file
65
bellos_scripts/string_manipulation.bellos
Normal file → Executable file
65
bellos_scripts/string_manipulation.bellos
Normal file → Executable file
@ -1,37 +1,54 @@
|
|||||||
#!/usr/bin/env bellos
|
#!/usr/bin/env bellos
|
||||||
|
|
||||||
# File: string_manipulation.bellos
|
# File: string_manipulation.bellos
|
||||||
|
# Demonstrating basic string manipulation in Bellos
|
||||||
|
|
||||||
# Demonstrating string manipulation
|
# String assignment
|
||||||
|
echo "String assignment:"
|
||||||
# String concatenation
|
|
||||||
first_name=John
|
first_name=John
|
||||||
last_name=Doe
|
last_name=Doe
|
||||||
full_name="$first_name $last_name"
|
echo "first_name: $first_name"
|
||||||
echo Full name: $full_name
|
echo "last_name: $last_name"
|
||||||
|
echo
|
||||||
|
|
||||||
# String length
|
# String concatenation
|
||||||
string="Hello, World!"
|
echo "String concatenation:"
|
||||||
echo The string '$string' has ${#string} characters.
|
full_name="$first_name $last_name"
|
||||||
|
echo full_name: $full_name
|
||||||
|
echo
|
||||||
|
|
||||||
# Substring extraction
|
# Using strings in commands
|
||||||
echo The first 5 characters are: ${string:0:5}
|
echo "Using strings in commands:"
|
||||||
|
greeting=Hello
|
||||||
|
echo "$greeting, $full_name!"
|
||||||
|
echo
|
||||||
|
|
||||||
# String replacement
|
# Quoting strings
|
||||||
|
echo "Quoting strings:"
|
||||||
sentence="The quick brown fox jumps over the lazy dog"
|
sentence="The quick brown fox jumps over the lazy dog"
|
||||||
echo Original sentence: $sentence
|
echo With double quotes: "$sentence"
|
||||||
new_sentence=${sentence/fox/cat}
|
echo With single quotes: '$sentence'
|
||||||
echo Modified sentence: $new_sentence
|
echo
|
||||||
|
|
||||||
# Converting to uppercase and lowercase
|
# Assigning command output to a variable
|
||||||
echo Uppercase: ${string^^}
|
echo "Assigning command output to a variable:"
|
||||||
echo Lowercase: ${string,,}
|
current_date=10-17-2025
|
||||||
|
echo "The current date is: $current_date"
|
||||||
|
echo
|
||||||
|
|
||||||
# Trimming whitespace
|
# Escaping special characters
|
||||||
padded_string=" trim me "
|
echo "Escaping special characters:"
|
||||||
echo Original string: '$padded_string'
|
path=/home/user/documents
|
||||||
trimmed_string="${padded_string#"${padded_string%%[![:space:]]*}"}" # Trim leading whitespace
|
echo "Path with slashes: $path"
|
||||||
trimmed_string="${trimmed_string%"${trimmed_string##*[![:space:]]}"}" # Trim trailing whitespace
|
echo "Dollar sign: $100"
|
||||||
echo Trimmed string: '$trimmed_string'
|
echo
|
||||||
|
|
||||||
|
# Using variables in paths
|
||||||
|
echo "Using variables in paths:"
|
||||||
|
username=johndoe
|
||||||
|
user_home=/home/$username
|
||||||
|
echo "User's home directory: $user_home"
|
||||||
|
echo
|
||||||
|
|
||||||
# Completion message
|
# Completion message
|
||||||
echo String manipulation operations completed.
|
echo "String manipulation operations completed."
|
||||||
|
Binary file not shown.
@ -18,8 +18,6 @@ use crate::utilities::utilities::{ASTNode, Token};
|
|||||||
pub struct Parser {
|
pub struct Parser {
|
||||||
tokens: Vec<Token>,
|
tokens: Vec<Token>,
|
||||||
position: usize,
|
position: usize,
|
||||||
recursion_depth: usize,
|
|
||||||
max_recursion_depth: usize,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parser {
|
impl Parser {
|
||||||
@ -27,199 +25,48 @@ impl Parser {
|
|||||||
Parser {
|
Parser {
|
||||||
tokens,
|
tokens,
|
||||||
position: 0,
|
position: 0,
|
||||||
recursion_depth: 0,
|
|
||||||
max_recursion_depth: 1000,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn increment_recursion(&mut self) -> Result<(), String> {
|
|
||||||
self.recursion_depth += 1;
|
|
||||||
if self.recursion_depth > self.max_recursion_depth {
|
|
||||||
Err("Maximum recursion depth exceeded".to_string())
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn decrement_recursion(&mut self) {
|
|
||||||
self.recursion_depth -= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parse(&mut self) -> Result<Vec<ASTNode>, String> {
|
pub fn parse(&mut self) -> Result<Vec<ASTNode>, String> {
|
||||||
let mut nodes = Vec::new();
|
let mut nodes = Vec::new();
|
||||||
while self.position < self.tokens.len() {
|
while self.position < self.tokens.len() {
|
||||||
|
self.skip_newlines();
|
||||||
|
if self.position >= self.tokens.len() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
nodes.push(self.parse_statement()?);
|
nodes.push(self.parse_statement()?);
|
||||||
self.consume_if(Token::Semicolon);
|
|
||||||
self.consume_if(Token::NewLine);
|
|
||||||
}
|
}
|
||||||
Ok(nodes)
|
Ok(nodes)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_statement(&mut self) -> Result<ASTNode, String> {
|
fn parse_statement(&mut self) -> Result<ASTNode, String> {
|
||||||
self.increment_recursion()?;
|
|
||||||
let result = if self.position >= self.tokens.len() {
|
|
||||||
Err("Unexpected end of input".to_string())
|
|
||||||
} else {
|
|
||||||
match &self.tokens[self.position] {
|
|
||||||
Token::Word(_) => self.parse_command_or_assignment(),
|
|
||||||
Token::LeftParen => self.parse_block(),
|
|
||||||
Token::If => self.parse_if(),
|
|
||||||
Token::While => self.parse_while(),
|
|
||||||
Token::For => self.parse_for(),
|
|
||||||
Token::Function => self.parse_function(),
|
|
||||||
_ => Err(format!(
|
|
||||||
"Unexpected token: {:?}",
|
|
||||||
self.tokens[self.position]
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
self.decrement_recursion();
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_command_or_assignment(&mut self) -> Result<ASTNode, String> {
|
|
||||||
let name = match &self.tokens[self.position] {
|
|
||||||
Token::Word(w) => w.clone(),
|
|
||||||
_ => {
|
|
||||||
return Err(format!(
|
|
||||||
"Expected word, found {:?}",
|
|
||||||
self.tokens[self.position]
|
|
||||||
))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
self.position += 1;
|
|
||||||
|
|
||||||
if self.position < self.tokens.len() && self.tokens[self.position] == Token::Assignment {
|
|
||||||
self.position += 1;
|
|
||||||
let value = self.parse_expression()?;
|
|
||||||
Ok(ASTNode::Assignment { name, value })
|
|
||||||
} else {
|
|
||||||
let mut args = Vec::new();
|
|
||||||
while self.position < self.tokens.len()
|
|
||||||
&& !matches!(
|
|
||||||
self.tokens[self.position],
|
|
||||||
Token::Pipe
|
|
||||||
| Token::Redirect(_)
|
|
||||||
| Token::Semicolon
|
|
||||||
| Token::NewLine
|
|
||||||
| Token::Ampersand
|
|
||||||
| Token::Assignment
|
|
||||||
)
|
|
||||||
{
|
|
||||||
match &self.tokens[self.position] {
|
|
||||||
Token::Word(w) => args.push(w.clone()),
|
|
||||||
Token::String(s) => args.push(s.clone()),
|
|
||||||
_ => break,
|
|
||||||
}
|
|
||||||
self.position += 1;
|
|
||||||
}
|
|
||||||
let command = ASTNode::Command { name, args };
|
|
||||||
self.parse_pipeline_or_redirect(command)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_expression(&mut self) -> Result<String, String> {
|
|
||||||
let mut expression = String::new();
|
|
||||||
let mut paren_count = 0;
|
|
||||||
|
|
||||||
while self.position < self.tokens.len() {
|
|
||||||
match &self.tokens[self.position] {
|
|
||||||
Token::Word(w) => expression.push_str(w),
|
|
||||||
Token::String(s) => {
|
|
||||||
expression.push('"');
|
|
||||||
expression.push_str(s);
|
|
||||||
expression.push('"');
|
|
||||||
}
|
|
||||||
Token::LeftParen => {
|
|
||||||
expression.push('(');
|
|
||||||
paren_count += 1;
|
|
||||||
}
|
|
||||||
Token::RightParen if paren_count > 0 => {
|
|
||||||
expression.push(')');
|
|
||||||
paren_count -= 1;
|
|
||||||
}
|
|
||||||
Token::Semicolon | Token::NewLine if paren_count == 0 => break,
|
|
||||||
Token::Assignment => expression.push('='),
|
|
||||||
_ if paren_count == 0 => break,
|
|
||||||
_ => expression.push_str(&format!("{:?}", self.tokens[self.position])),
|
|
||||||
}
|
|
||||||
self.position += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if paren_count != 0 {
|
|
||||||
return Err("Mismatched parentheses in expression".to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(expression)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_pipeline_or_redirect(&mut self, left: ASTNode) -> Result<ASTNode, String> {
|
|
||||||
if self.position >= self.tokens.len() {
|
if self.position >= self.tokens.len() {
|
||||||
return Ok(left);
|
return Err("Unexpected end of input".to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
match &self.tokens[self.position] {
|
match &self.tokens[self.position] {
|
||||||
Token::Pipe => {
|
Token::Word(w) if w.eq_ignore_ascii_case("if") => self.parse_if(),
|
||||||
self.position += 1;
|
Token::Word(w) if w.eq_ignore_ascii_case("while") => self.parse_while(),
|
||||||
let right = self.parse_command_or_assignment()?;
|
Token::Word(w) if w.eq_ignore_ascii_case("for") => self.parse_for(),
|
||||||
let pipeline = ASTNode::Pipeline(vec![left, right]);
|
Token::Word(w) if w.eq_ignore_ascii_case("case") => self.parse_case(),
|
||||||
self.parse_pipeline_or_redirect(pipeline)
|
Token::Word(w) if w.eq_ignore_ascii_case("function") => self.parse_function(),
|
||||||
}
|
_ => self.parse_command_or_assignment(),
|
||||||
Token::Redirect(direction) => {
|
|
||||||
self.position += 1;
|
|
||||||
let target = if self.position < self.tokens.len() {
|
|
||||||
match &self.tokens[self.position] {
|
|
||||||
Token::Word(w) => w.clone(),
|
|
||||||
Token::String(s) => s.clone(),
|
|
||||||
_ => {
|
|
||||||
return Err(format!(
|
|
||||||
"Expected word after redirect, found {:?}",
|
|
||||||
self.tokens[self.position]
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return Err("Unexpected end of input after redirect".to_string());
|
|
||||||
};
|
|
||||||
self.position += 1;
|
|
||||||
let redirect = ASTNode::Redirect {
|
|
||||||
node: Box::new(left),
|
|
||||||
direction: direction.clone(),
|
|
||||||
target,
|
|
||||||
};
|
|
||||||
self.parse_pipeline_or_redirect(redirect)
|
|
||||||
}
|
|
||||||
Token::Ampersand => {
|
|
||||||
self.position += 1;
|
|
||||||
Ok(ASTNode::Background(Box::new(left)))
|
|
||||||
}
|
|
||||||
_ => Ok(left),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_block(&mut self) -> Result<ASTNode, String> {
|
|
||||||
self.position += 1; // Consume left paren
|
|
||||||
let mut statements = Vec::new();
|
|
||||||
while self.position < self.tokens.len() && self.tokens[self.position] != Token::RightParen {
|
|
||||||
statements.push(self.parse_statement()?);
|
|
||||||
self.consume_if(Token::Semicolon);
|
|
||||||
self.consume_if(Token::NewLine);
|
|
||||||
}
|
|
||||||
self.expect_token(Token::RightParen)?;
|
|
||||||
Ok(ASTNode::Block(statements))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_if(&mut self) -> Result<ASTNode, String> {
|
fn parse_if(&mut self) -> Result<ASTNode, String> {
|
||||||
self.position += 1; // Consume 'if'
|
self.position += 1; // Consume 'if'
|
||||||
let condition = Box::new(self.parse_command()?);
|
let condition = Box::new(self.parse_command()?);
|
||||||
self.expect_token(Token::Then)?;
|
self.skip_newlines_and_expect("then")?;
|
||||||
let then_block = Box::new(self.parse_block()?);
|
let then_block = Box::new(self.parse_block("else", "fi")?);
|
||||||
let else_block = if self.consume_if(Token::Else) {
|
let else_block = if self.current_token_is("else") {
|
||||||
Some(Box::new(self.parse_block()?))
|
self.position += 1;
|
||||||
|
self.skip_newlines();
|
||||||
|
Some(Box::new(self.parse_block("fi", "fi")?))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
self.expect_token(Token::Fi)?;
|
self.skip_newlines_and_expect("fi")?;
|
||||||
Ok(ASTNode::If {
|
Ok(ASTNode::If {
|
||||||
condition,
|
condition,
|
||||||
then_block,
|
then_block,
|
||||||
@ -230,65 +77,70 @@ impl Parser {
|
|||||||
fn parse_while(&mut self) -> Result<ASTNode, String> {
|
fn parse_while(&mut self) -> Result<ASTNode, String> {
|
||||||
self.position += 1; // Consume 'while'
|
self.position += 1; // Consume 'while'
|
||||||
let condition = Box::new(self.parse_command()?);
|
let condition = Box::new(self.parse_command()?);
|
||||||
self.expect_token(Token::Do)?;
|
self.skip_newlines_and_expect("do")?;
|
||||||
let block = Box::new(self.parse_block()?);
|
let block = Box::new(self.parse_block("done", "done")?);
|
||||||
self.expect_token(Token::Done)?;
|
self.skip_newlines_and_expect("done")?;
|
||||||
Ok(ASTNode::While { condition, block })
|
Ok(ASTNode::While { condition, block })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_for(&mut self) -> Result<ASTNode, String> {
|
fn parse_for(&mut self) -> Result<ASTNode, String> {
|
||||||
self.position += 1; // Consume 'for'
|
self.position += 1; // Consume 'for'
|
||||||
let var = match &self.tokens[self.position] {
|
let var = self.expect_word()?;
|
||||||
Token::Word(w) => w.clone(),
|
self.skip_newlines_and_expect("in")?;
|
||||||
_ => return Err("Expected variable name after 'for'".to_string()),
|
let list = self.parse_list()?;
|
||||||
};
|
self.skip_newlines_and_expect("do")?;
|
||||||
self.position += 1;
|
let block = Box::new(self.parse_block("done", "done")?);
|
||||||
self.expect_token(Token::In)?;
|
self.skip_newlines_and_expect("done")?;
|
||||||
let mut list = Vec::new();
|
|
||||||
while self.position < self.tokens.len() && self.tokens[self.position] != Token::Do {
|
|
||||||
match &self.tokens[self.position] {
|
|
||||||
Token::Word(w) => list.push(w.clone()),
|
|
||||||
Token::String(s) => list.push(s.clone()),
|
|
||||||
_ => break,
|
|
||||||
}
|
|
||||||
self.position += 1;
|
|
||||||
}
|
|
||||||
self.expect_token(Token::Do)?;
|
|
||||||
let block = Box::new(self.parse_block()?);
|
|
||||||
self.expect_token(Token::Done)?;
|
|
||||||
Ok(ASTNode::For { var, list, block })
|
Ok(ASTNode::For { var, list, block })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_case(&mut self) -> Result<ASTNode, String> {
|
||||||
|
self.position += 1; // Consume 'case'
|
||||||
|
let var = self.parse_expression()?;
|
||||||
|
self.skip_newlines_and_expect("in")?;
|
||||||
|
let mut cases = Vec::new();
|
||||||
|
while !self.current_token_is("esac") {
|
||||||
|
self.skip_newlines();
|
||||||
|
let pattern = self.parse_expression()?;
|
||||||
|
self.expect_token(&Token::RightParen)?;
|
||||||
|
let block = self.parse_block(";;", "esac")?;
|
||||||
|
cases.push((pattern, block));
|
||||||
|
self.skip_newlines();
|
||||||
|
if self.current_token_is(";;") {
|
||||||
|
self.position += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.skip_newlines_and_expect("esac")?;
|
||||||
|
Ok(ASTNode::Case { var, cases })
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_function(&mut self) -> Result<ASTNode, String> {
|
fn parse_function(&mut self) -> Result<ASTNode, String> {
|
||||||
self.position += 1; // Consume 'function'
|
self.position += 1; // Consume 'function'
|
||||||
let name = match &self.tokens[self.position] {
|
let name = self.expect_word()?;
|
||||||
Token::Word(w) => w.clone(),
|
self.skip_newlines();
|
||||||
_ => {
|
self.expect_token(&Token::LeftParen)?;
|
||||||
return Err(format!(
|
self.skip_newlines();
|
||||||
"Expected function name, found {:?}",
|
let body = Box::new(self.parse_block(")", ")")?);
|
||||||
self.tokens[self.position]
|
self.expect_token(&Token::RightParen)?;
|
||||||
))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
self.position += 1;
|
|
||||||
let body = Box::new(self.parse_block()?);
|
|
||||||
Ok(ASTNode::Function { name, body })
|
Ok(ASTNode::Function { name, body })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_block(&mut self, end_token1: &str, end_token2: &str) -> Result<ASTNode, String> {
|
||||||
|
let mut statements = Vec::new();
|
||||||
|
while !self.current_token_is(end_token1) && !self.current_token_is(end_token2) {
|
||||||
|
self.skip_newlines();
|
||||||
|
if self.current_token_is(end_token1) || self.current_token_is(end_token2) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
statements.push(self.parse_statement()?);
|
||||||
|
}
|
||||||
|
Ok(ASTNode::Block(statements))
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_command(&mut self) -> Result<ASTNode, String> {
|
fn parse_command(&mut self) -> Result<ASTNode, String> {
|
||||||
let mut args = Vec::new();
|
let mut args = Vec::new();
|
||||||
while self.position < self.tokens.len()
|
while self.position < self.tokens.len() && !self.is_command_end() {
|
||||||
&& !matches!(
|
args.push(self.expect_word()?);
|
||||||
self.tokens[self.position],
|
|
||||||
Token::Then | Token::Do | Token::Done | Token::Fi | Token::Else
|
|
||||||
)
|
|
||||||
{
|
|
||||||
match &self.tokens[self.position] {
|
|
||||||
Token::Word(w) => args.push(w.clone()),
|
|
||||||
Token::String(s) => args.push(s.clone()),
|
|
||||||
_ => break,
|
|
||||||
}
|
|
||||||
self.position += 1;
|
|
||||||
}
|
}
|
||||||
if args.is_empty() {
|
if args.is_empty() {
|
||||||
Err("Expected command".to_string())
|
Err("Expected command".to_string())
|
||||||
@ -300,25 +152,109 @@ impl Parser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expect_token(&mut self, expected: Token) -> Result<(), String> {
|
fn parse_list(&mut self) -> Result<Vec<String>, String> {
|
||||||
if self.position < self.tokens.len() && self.tokens[self.position] == expected {
|
let mut list = Vec::new();
|
||||||
|
while !self.current_token_is("do") {
|
||||||
|
list.push(self.expect_word()?);
|
||||||
|
self.skip_newlines();
|
||||||
|
}
|
||||||
|
Ok(list)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_expression(&mut self) -> Result<String, String> {
|
||||||
|
self.expect_word()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expect_word(&mut self) -> Result<String, String> {
|
||||||
|
if self.position >= self.tokens.len() {
|
||||||
|
return Err("Unexpected end of input".to_string());
|
||||||
|
}
|
||||||
|
match &self.tokens[self.position] {
|
||||||
|
Token::Word(w) | Token::String(w) => {
|
||||||
|
self.position += 1;
|
||||||
|
Ok(w.clone())
|
||||||
|
}
|
||||||
|
_ => Err(format!(
|
||||||
|
"Expected word, found {:?}",
|
||||||
|
self.tokens[self.position]
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expect_token(&mut self, expected: &Token) -> Result<(), String> {
|
||||||
|
if self.position >= self.tokens.len() {
|
||||||
|
return Err(format!("Expected {:?}, found end of input", expected));
|
||||||
|
}
|
||||||
|
if self.tokens[self.position] == *expected {
|
||||||
self.position += 1;
|
self.position += 1;
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(format!(
|
Err(format!(
|
||||||
"Expected {:?}, found {:?}",
|
"Expected {:?}, found {:?}",
|
||||||
expected,
|
expected, self.tokens[self.position]
|
||||||
self.tokens.get(self.position).unwrap_or(&Token::NewLine)
|
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn consume_if(&mut self, token: Token) -> bool {
|
fn current_token_is(&self, token: &str) -> bool {
|
||||||
if self.position < self.tokens.len() && self.tokens[self.position] == token {
|
if self.position >= self.tokens.len() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
match &self.tokens[self.position] {
|
||||||
|
Token::Word(w) => w.eq_ignore_ascii_case(token),
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn skip_newlines(&mut self) {
|
||||||
|
while self.position < self.tokens.len() && self.tokens[self.position] == Token::NewLine {
|
||||||
self.position += 1;
|
self.position += 1;
|
||||||
true
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn skip_newlines_and_expect(&mut self, expected: &str) -> Result<(), String> {
|
||||||
|
self.skip_newlines();
|
||||||
|
if self.position >= self.tokens.len() {
|
||||||
|
return Err(format!("Expected {}, found end of input", expected));
|
||||||
|
}
|
||||||
|
if self.current_token_is(expected) {
|
||||||
|
self.position += 1;
|
||||||
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
false
|
Err(format!(
|
||||||
|
"Expected {}, found {:?}",
|
||||||
|
expected, self.tokens[self.position]
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_command_end(&self) -> bool {
|
||||||
|
self.position >= self.tokens.len()
|
||||||
|
|| matches!(
|
||||||
|
self.tokens[self.position],
|
||||||
|
Token::Semicolon | Token::NewLine
|
||||||
|
)
|
||||||
|
|| self.current_token_is("then")
|
||||||
|
|| self.current_token_is("do")
|
||||||
|
|| self.current_token_is("done")
|
||||||
|
|| self.current_token_is("fi")
|
||||||
|
|| self.current_token_is("else")
|
||||||
|
|| self.current_token_is("elif")
|
||||||
|
|| self.current_token_is("esac")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_command_or_assignment(&mut self) -> Result<ASTNode, String> {
|
||||||
|
let name = self.expect_word()?;
|
||||||
|
if self.position < self.tokens.len() && self.tokens[self.position] == Token::Assignment {
|
||||||
|
self.position += 1;
|
||||||
|
let value = self.parse_expression()?;
|
||||||
|
Ok(ASTNode::Assignment { name, value })
|
||||||
|
} else {
|
||||||
|
let mut args = Vec::new();
|
||||||
|
while self.position < self.tokens.len() && !self.is_command_end() {
|
||||||
|
args.push(self.expect_word()?);
|
||||||
|
}
|
||||||
|
Ok(ASTNode::Command { name, args })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,6 +34,8 @@ pub enum Token {
|
|||||||
Done,
|
Done,
|
||||||
For,
|
For,
|
||||||
In,
|
In,
|
||||||
|
Case,
|
||||||
|
Esac,
|
||||||
Function,
|
Function,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,6 +92,10 @@ pub enum ASTNode {
|
|||||||
body: Box<ASTNode>,
|
body: Box<ASTNode>,
|
||||||
},
|
},
|
||||||
Background(Box<ASTNode>),
|
Background(Box<ASTNode>),
|
||||||
|
Case {
|
||||||
|
var: String,
|
||||||
|
cases: Vec<(String, ASTNode)>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ASTNode {
|
impl ASTNode {
|
||||||
|
Loading…
Reference in New Issue
Block a user