started work on c compiler
This commit is contained in:
@@ -0,0 +1,265 @@
|
||||
// ============================================================================
|
||||
// Token Types
|
||||
// ============================================================================
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum TokenType {
|
||||
// Keywords
|
||||
Int,
|
||||
If,
|
||||
Else,
|
||||
While,
|
||||
Return,
|
||||
|
||||
// Identifiers and literals
|
||||
Identifier(String),
|
||||
Number(i32),
|
||||
|
||||
// Operators
|
||||
Plus,
|
||||
Minus,
|
||||
Star,
|
||||
Slash,
|
||||
Assign,
|
||||
Eq,
|
||||
Ne,
|
||||
Lt,
|
||||
Gt,
|
||||
Le,
|
||||
Ge,
|
||||
|
||||
// Delimiters
|
||||
LParen,
|
||||
RParen,
|
||||
LBrace,
|
||||
RBrace,
|
||||
Semicolon,
|
||||
Comma,
|
||||
|
||||
Eof,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Token {
|
||||
pub token_type: TokenType,
|
||||
pub line: usize,
|
||||
pub col: usize,
|
||||
}
|
||||
|
||||
impl Token {
|
||||
pub fn new(token_type: TokenType, line: usize, col: usize) -> Self {
|
||||
Self {
|
||||
token_type,
|
||||
line,
|
||||
col,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Lexer
|
||||
// ============================================================================
|
||||
|
||||
pub struct Lexer {
|
||||
source: Vec<char>,
|
||||
pos: usize,
|
||||
line: usize,
|
||||
col: usize,
|
||||
}
|
||||
|
||||
impl Lexer {
|
||||
pub fn new(source: &str) -> Self {
|
||||
Self {
|
||||
source: source.chars().collect(),
|
||||
pos: 0,
|
||||
line: 1,
|
||||
col: 1,
|
||||
}
|
||||
}
|
||||
|
||||
fn error(&self, msg: &str) -> String {
|
||||
format!(
|
||||
"Lexer error at line {}, col {}: {}",
|
||||
self.line, self.col, msg
|
||||
)
|
||||
}
|
||||
|
||||
fn peek(&self, offset: usize) -> Option<char> {
|
||||
self.source.get(self.pos + offset).copied()
|
||||
}
|
||||
|
||||
fn advance(&mut self) -> Option<char> {
|
||||
if self.pos >= self.source.len() {
|
||||
return None;
|
||||
}
|
||||
let ch = self.source[self.pos];
|
||||
self.pos += 1;
|
||||
if ch == '\n' {
|
||||
self.line += 1;
|
||||
self.col = 1;
|
||||
} else {
|
||||
self.col += 1;
|
||||
}
|
||||
Some(ch)
|
||||
}
|
||||
|
||||
fn skip_whitespace(&mut self) {
|
||||
while let Some(ch) = self.peek(0) {
|
||||
if ch.is_whitespace() {
|
||||
self.advance();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn skip_comment(&mut self) {
|
||||
if self.peek(0) == Some('/') && self.peek(1) == Some('/') {
|
||||
while let Some(ch) = self.peek(0) {
|
||||
if ch == '\n' {
|
||||
break;
|
||||
}
|
||||
self.advance();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn read_number(&mut self) -> i32 {
|
||||
let mut num_str = String::new();
|
||||
while let Some(ch) = self.peek(0) {
|
||||
if ch.is_ascii_digit() {
|
||||
num_str.push(ch);
|
||||
self.advance();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
num_str.parse().unwrap_or(0)
|
||||
}
|
||||
|
||||
fn read_identifier(&mut self) -> String {
|
||||
let mut ident = String::new();
|
||||
while let Some(ch) = self.peek(0) {
|
||||
if ch.is_alphanumeric() || ch == '_' {
|
||||
ident.push(ch);
|
||||
self.advance();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
ident
|
||||
}
|
||||
|
||||
pub fn tokenize(&mut self) -> Result<Vec<Token>, String> {
|
||||
let mut tokens = Vec::new();
|
||||
|
||||
loop {
|
||||
self.skip_whitespace();
|
||||
self.skip_comment();
|
||||
|
||||
if self.pos >= self.source.len() {
|
||||
break;
|
||||
}
|
||||
|
||||
let line = self.line;
|
||||
let col = self.col;
|
||||
let ch = self.peek(0).unwrap();
|
||||
|
||||
let token_type = if ch.is_ascii_digit() {
|
||||
let num = self.read_number();
|
||||
TokenType::Number(num)
|
||||
} else if ch.is_alphabetic() || ch == '_' {
|
||||
let ident = self.read_identifier();
|
||||
match ident.as_str() {
|
||||
"int" => TokenType::Int,
|
||||
"if" => TokenType::If,
|
||||
"else" => TokenType::Else,
|
||||
"while" => TokenType::While,
|
||||
"return" => TokenType::Return,
|
||||
_ => TokenType::Identifier(ident),
|
||||
}
|
||||
} else {
|
||||
match ch {
|
||||
'=' if self.peek(1) == Some('=') => {
|
||||
self.advance();
|
||||
self.advance();
|
||||
TokenType::Eq
|
||||
}
|
||||
'!' if self.peek(1) == Some('=') => {
|
||||
self.advance();
|
||||
self.advance();
|
||||
TokenType::Ne
|
||||
}
|
||||
'<' if self.peek(1) == Some('=') => {
|
||||
self.advance();
|
||||
self.advance();
|
||||
TokenType::Le
|
||||
}
|
||||
'>' if self.peek(1) == Some('=') => {
|
||||
self.advance();
|
||||
self.advance();
|
||||
TokenType::Ge
|
||||
}
|
||||
'+' => {
|
||||
self.advance();
|
||||
TokenType::Plus
|
||||
}
|
||||
'-' => {
|
||||
self.advance();
|
||||
TokenType::Minus
|
||||
}
|
||||
'*' => {
|
||||
self.advance();
|
||||
TokenType::Star
|
||||
}
|
||||
'/' => {
|
||||
self.advance();
|
||||
TokenType::Slash
|
||||
}
|
||||
'=' => {
|
||||
self.advance();
|
||||
TokenType::Assign
|
||||
}
|
||||
'<' => {
|
||||
self.advance();
|
||||
TokenType::Lt
|
||||
}
|
||||
'>' => {
|
||||
self.advance();
|
||||
TokenType::Gt
|
||||
}
|
||||
'(' => {
|
||||
self.advance();
|
||||
TokenType::LParen
|
||||
}
|
||||
')' => {
|
||||
self.advance();
|
||||
TokenType::RParen
|
||||
}
|
||||
'{' => {
|
||||
self.advance();
|
||||
TokenType::LBrace
|
||||
}
|
||||
'}' => {
|
||||
self.advance();
|
||||
TokenType::RBrace
|
||||
}
|
||||
';' => {
|
||||
self.advance();
|
||||
TokenType::Semicolon
|
||||
}
|
||||
',' => {
|
||||
self.advance();
|
||||
TokenType::Comma
|
||||
}
|
||||
_ => return Err(self.error(&format!("Unexpected character: {}", ch))),
|
||||
}
|
||||
};
|
||||
|
||||
tokens.push(Token::new(token_type, line, col));
|
||||
}
|
||||
|
||||
tokens.push(Token::new(TokenType::Eof, self.line, self.col));
|
||||
Ok(tokens)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user