This commit is contained in:
Leonard Hecker 2025-10-21 22:17:19 +02:00
parent d3d3b3b624
commit 0b0da89674
3 changed files with 99 additions and 91 deletions

View file

@ -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<NodeCell<'a>>, next: Option<NodeCell<'a>> },
/// Regex match block: pattern, body, next statement
RegexMatch { pattern: &'a Charset, body: NodeCell<'a>, next: Option<NodeCell<'a>> },
/// Yield statement: color name
Yield { color: &'a str, next: Option<NodeCell<'a>> },
/// Function call: name
Call { name: &'a str, next: Option<NodeCell<'a>> },
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<NodeCell<'a>> {
fn parse_block(
&mut self,
next: NodeCell<'a>,
) -> ParseResult<Option<(NodeCell<'a>, NodeCell<'a>)>> {
self.expect_token(Token::LeftBrace)?;
let mut statements = None;
let mut last_statement = None;
let mut statements: Option<(&RefCell<Node<'a>>, &RefCell<Node<'a>>)> = 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<NodeCell<'a>> {
fn parse_statement(&mut self, next: NodeCell<'a>) -> ParseResult<NodeCell<'a>> {
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<NodeCell<'a>> {
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<NodeCell<'a>> {
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<NodeCell<'a>> {
fn parse_regex(&mut self) -> ParseResult<NodeCell<'a>> {
todo!()
}
fn parse_yield(&mut self, next: NodeCell<'a>) -> ParseResult<NodeCell<'a>> {
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<NodeCell<'a>> {
fn parse_call(&mut self, next: NodeCell<'a>) -> ParseResult<NodeCell<'a>> {
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<NodeCell<'a>>) {
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<NodeCell<'a>> {
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;
}

View file

@ -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),
}
}

View file

@ -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; }
}
}