diff --git a/crates/lsh-gen/src/compiler/frontend.rs b/crates/lsh-gen/src/compiler/frontend.rs index bad3181..00a6588 100644 --- a/crates/lsh-gen/src/compiler/frontend.rs +++ b/crates/lsh-gen/src/compiler/frontend.rs @@ -21,16 +21,26 @@ pub struct Function<'a> { pub body: NodeCell<'a>, } +#[derive(Debug)] +pub enum Condition<'a> { + JumpIfNotMatchCharset { charset: &'a Charset }, + JumpIfNotMatchPrefix { prefix: &'a str }, + JumpIfNotMatchPrefixInsensitive { prefix: &'a str }, +} + #[derive(Debug)] pub enum Node<'a> { - /// Block containing statements - Block { statements: Option>, next: Option> }, - /// Regex match block: pattern, body, next statement - RegexMatch { pattern: &'a Charset, body: NodeCell<'a>, next: Option> }, - /// Yield statement: color name - Yield { color: &'a str, next: Option> }, - /// Function call: name - Call { name: &'a str, next: Option> }, + Return, + Jump { destination: NodeCell<'a> }, + If { condition: Condition<'a>, then_branch: NodeCell<'a>, else_branch: NodeCell<'a> }, + Yield { color: &'a str, next: NodeCell<'a> }, + Call { name: &'a str, next: NodeCell<'a> }, +} + +impl<'a> Node<'a> { + fn set_next(&mut self, _next: NodeCell<'a>) { + todo!() + } } pub struct Frontend<'a> { @@ -84,12 +94,11 @@ struct Parser<'a, 'src> { } impl<'a, 'src> Parser<'a, 'src> { - /// Entrypoint. Call this. - fn run(&mut self) -> ParseResult<()> { + pub fn run(&mut self) -> ParseResult<()> { self.advance(); while !matches!(self.current_token, Token::Eof) { - let function = self.parse_function()?; - self.frontend.functions.push(function); + let f = self.parse_function()?; + self.frontend.functions.push(f); } Ok(()) } @@ -106,60 +115,72 @@ impl<'a, 'src> Parser<'a, 'src> { self.expect_token(Token::LeftParen)?; self.expect_token(Token::RightParen)?; - let body = self.parse_block()?; - - Ok(Function { name, body }) + let ret = self.alloc_node(Node::Return); + let block = self.parse_block(ret)?; + Ok(Function { name, body: block.map_or(ret, |(first, _)| first) }) } - fn parse_block(&mut self) -> ParseResult> { + fn parse_block( + &mut self, + next: NodeCell<'a>, + ) -> ParseResult, NodeCell<'a>)>> { self.expect_token(Token::LeftBrace)?; - let mut statements = None; - let mut last_statement = None; + let mut statements: Option<(&RefCell>, &RefCell>)> = None; while !matches!(self.current_token, Token::RightBrace | Token::Eof) { - let statement = self.parse_statement()?; - - if statements.is_none() { - statements = Some(statement); - last_statement = Some(statement); + let statement = self.parse_statement(next)?; + if let Some((_, last)) = &mut statements { + last.borrow_mut().set_next(statement); + *last = statement; } else { - last_statement.unwrap().borrow_mut().set_next(Some(statement)); - last_statement = Some(statement); + statements = Some((statement, statement)); } } self.expect_token(Token::RightBrace)?; - Ok(self.alloc_node(Node::Block { statements, next: None })) + Ok(statements) } - fn parse_statement(&mut self) -> ParseResult> { + fn parse_statement(&mut self, next: NodeCell<'a>) -> ParseResult> { match &self.current_token { - Token::Regex(_) => self.parse_regex_match(), - Token::Yield => self.parse_yield(), - Token::Identifier(_) => self.parse_call(), + Token::Return => { + self.advance(); + self.expect_token(Token::Semicolon)?; + Ok(self.alloc_node(Node::Return)) + } + Token::If => self.parse_if_statement(next), + Token::Yield => self.parse_yield(next), + Token::Identifier(_) => self.parse_call(next), _ => raise!("Unexpected token: {:?}", self.current_token), } } - fn parse_regex_match(&mut self) -> ParseResult> { - let pattern = match &self.current_token { - Token::Regex(r) => { - let charset = - Charset::from_regex(r).map_err(|e| format!("Invalid regex: {}", e))?; - self.frontend.charsets.intern(self.frontend.arena, &charset) + fn parse_if_statement(&mut self, next: NodeCell<'a>) -> ParseResult> { + loop { + self.expect_token(Token::If)?; + self.parse_regex()?; + self.parse_block(next)?; + + if !matches!(self.current_token, Token::Else) { + break; } - _ => raise!("Expected regex pattern"), - }; - self.advance(); - let body = self.parse_block()?; + self.advance(); - Ok(self.alloc_node(Node::RegexMatch { pattern, body, next: None })) + if !matches!(self.current_token, Token::If) { + self.parse_block(next)?; + break; + } + } } - fn parse_yield(&mut self) -> ParseResult> { + fn parse_regex(&mut self) -> ParseResult> { + todo!() + } + + fn parse_yield(&mut self, next: NodeCell<'a>) -> ParseResult> { self.expect_token(Token::Yield)?; let color = match &self.current_token { @@ -170,10 +191,10 @@ impl<'a, 'src> Parser<'a, 'src> { self.expect_token(Token::Semicolon)?; - Ok(self.alloc_node(Node::Yield { color, next: None })) + Ok(self.alloc_node(Node::Yield { color, next })) } - fn parse_call(&mut self) -> ParseResult> { + fn parse_call(&mut self, next: NodeCell<'a>) -> ParseResult> { let name = match &self.current_token { Token::Identifier(n) => self.frontend.strings.intern(self.frontend.arena, n), _ => raise!("Expected function name"), @@ -184,7 +205,7 @@ impl<'a, 'src> Parser<'a, 'src> { self.expect_token(Token::RightParen)?; self.expect_token(Token::Semicolon)?; - Ok(self.alloc_node(Node::Call { name, next: None })) + Ok(self.alloc_node(Node::Call { name, next })) } fn expect_token(&mut self, expected: Token) -> ParseResult<()> { @@ -205,28 +226,6 @@ impl<'a, 'src> Parser<'a, 'src> { } } -impl<'a> Node<'a> { - /// Helper method to set the next pointer for any node type - fn set_next(&mut self, next: Option>) { - match self { - Node::Block { next: n, .. } - | Node::RegexMatch { next: n, .. } - | Node::Yield { next: n, .. } - | Node::Call { next: n, .. } => *n = next, - } - } - - /// Helper method to get the next pointer for any node type - pub fn next(&self) -> Option> { - match self { - Node::Block { next, .. } - | Node::RegexMatch { next, .. } - | Node::Yield { next, .. } - | Node::Call { next, .. } => *next, - } - } -} - trait Intern<'a, T: ?Sized> { fn intern(&mut self, arena: &'a Arena, item: &T) -> &'a T; } diff --git a/crates/lsh-gen/src/compiler/tokenizer.rs b/crates/lsh-gen/src/compiler/tokenizer.rs index 5db99f8..cb62111 100644 --- a/crates/lsh-gen/src/compiler/tokenizer.rs +++ b/crates/lsh-gen/src/compiler/tokenizer.rs @@ -34,8 +34,11 @@ pub enum Token { Regex(String), // Keywords + Return, Fn, Yield, + If, + Else, // Punctuation LeftBrace, @@ -105,18 +108,17 @@ impl<'a> Tokenizer<'a> { fn read_regex(&mut self) -> Token { let mut regex = String::new(); - while let Some(&ch) = self.peek() { + while let Some(ch) = self.advance() { if ch == '/' { - self.advance(); // consume closing '/' break; - } else if ch == '\\' { - self.advance(); // consume backslash - regex.push(ch); - if let Some(escaped) = self.advance() { - regex.push(escaped); - } + } + + // Unescape \/ -> / + if ch == '\\' && self.peek() == Some(&'/') { + self.advance(); + regex.push('/'); } else { - regex.push(self.advance().unwrap()); + regex.push(ch); } } @@ -136,8 +138,11 @@ impl<'a> Tokenizer<'a> { } match value.as_str() { + "return" => Token::Return, "fn" => Token::Fn, "yield" => Token::Yield, + "if" => Token::If, + "else" => Token::Else, _ => Token::Identifier(value), } } diff --git a/lsh/COMMIT_EDITMSG.lsh b/lsh/COMMIT_EDITMSG.lsh index 15e9e32..5c9b7e7 100644 --- a/lsh/COMMIT_EDITMSG.lsh +++ b/lsh/COMMIT_EDITMSG.lsh @@ -1,23 +1,27 @@ fn main() { - /#/ { - yield Comment; + loop { + /#/ { + yield Comment; - /\tdeleted:.*/ { yield BrightRed; } - /\tmodified:.*/ { yield BrightBlue; } - /\tnew file:.*/ { yield BrightGreen; } - /\trenamed:.*/ { yield BrightBlue; } - } + if /\tdeleted:.*/ { yield BrightRed; } + else if /\tmodified:.*/ { yield BrightBlue; } + else if /\tnew file:.*/ { yield BrightGreen; } + else if /\trenamed:.*/ { yield BrightBlue; } + } - /diff --git.*/ { - yield BrightBlue; - diff(); + /diff --git.*/ { + yield BrightBlue; + diff(); + } } } fn diff() { - /diff.*/ { yield BrightBlue; } - /---.*/ { yield BrightBlue; } - /\+\+\+.*/ { yield BrightBlue; } - /-.*/ { yield BrightRed; } - /\+.*/ { yield BrightGreen; } + loop { + if /diff.*/ { yield BrightBlue; } + else if /---.*/ { yield BrightBlue; } + else if /\+\+\+.*/ { yield BrightBlue; } + else if /-.*/ { yield BrightRed; } + else if /\+.*/ { yield BrightGreen; } + } }