Add basic parsing of let statements
This commit is contained in:
		
							parent
							
								
									1b28558dd8
								
							
						
					
					
						commit
						bea027d730
					
				
							
								
								
									
										5
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | ||||
| # This file is automatically @generated by Cargo. | ||||
| # It is not intended for manual editing. | ||||
| [[package]] | ||||
| name = "Reid" | ||||
| version = "0.1.0" | ||||
							
								
								
									
										1
									
								
								reid_src/test.reid
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								reid_src/test.reid
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| let otus = "gotus"; | ||||
							
								
								
									
										24
									
								
								src/errors.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								src/errors.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,24 @@ | ||||
| use super::parser::Position; | ||||
| use std::io; | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub enum GenericError { | ||||
|     StdIOError(io::Error), | ||||
| } | ||||
| 
 | ||||
| impl From<io::Error> for GenericError { | ||||
|     fn from(error: io::Error) -> Self { | ||||
|         Self::StdIOError(error) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub enum CompilerError { | ||||
|     Fatal, | ||||
|     PeekFailed, | ||||
|     ExpectedToken(Position, char), | ||||
|     ExpectedExpression(Position, Box<CompilerError>), | ||||
|     ExpectedIdent(Position), | ||||
|     ExpectedStatement(Position), | ||||
|     ExpectedPattern(Position), | ||||
| } | ||||
							
								
								
									
										14
									
								
								src/file_io.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/file_io.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,14 @@ | ||||
| use std::fs::File; | ||||
| use std::io::prelude::*; | ||||
| use std::io::BufReader; | ||||
| use std::path::Path; | ||||
| 
 | ||||
| use super::errors::GenericError; | ||||
| 
 | ||||
| pub fn open_file(path: &Path) -> Result<String, GenericError> { | ||||
|     let file = File::open(path)?; | ||||
|     let mut reader = BufReader::new(file); | ||||
|     let mut text = String::new(); | ||||
|     reader.read_to_string(&mut text)?; | ||||
|     Ok(text) | ||||
| } | ||||
							
								
								
									
										12
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								src/main.rs
									
									
									
									
									
								
							| @ -1,3 +1,13 @@ | ||||
| mod errors; | ||||
| mod file_io; | ||||
| mod parser; | ||||
| 
 | ||||
| use file_io::open_file; | ||||
| use parser::{ParsedReid, Parser}; | ||||
| use std::path::Path; | ||||
| 
 | ||||
| fn main() { | ||||
|     println!("Hello, world!"); | ||||
|     let path = Path::new("reid_src/test.reid"); | ||||
|     let reid = Parser::from(open_file(&path).ok().unwrap()).parse(); | ||||
|     println!("Parsed: {:?}", reid); | ||||
| } | ||||
|  | ||||
							
								
								
									
										327
									
								
								src/parser.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										327
									
								
								src/parser.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,327 @@ | ||||
| use super::errors::CompilerError; | ||||
| 
 | ||||
| type Ident = String; | ||||
| 
 | ||||
| const ALLOWED_IDENT_CHARS: [char; 38] = [ | ||||
|     'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', | ||||
|     't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '_', '-', | ||||
| ]; | ||||
| const ALLOWED_IDENT_BEGIN_CHARS: [char; 26] = [ | ||||
|     'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', | ||||
|     't', 'u', 'v', 'w', 'x', 'y', 'z', | ||||
| ]; | ||||
| 
 | ||||
| pub struct Parser { | ||||
|     text: Vec<char>, | ||||
|     cursor: usize, | ||||
|     line_number: usize, | ||||
|     character_number: usize, | ||||
| } | ||||
| 
 | ||||
| impl Parser { | ||||
|     pub fn from(text: String) -> Self { | ||||
|         Parser { | ||||
|             text: text.chars().collect(), | ||||
|             cursor: 0, | ||||
|             line_number: 0, | ||||
|             character_number: 0, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn parse(mut self) -> Result<ParsedReid, CompilerError> { | ||||
|         let mut exp_list = Vec::new(); | ||||
| 
 | ||||
|         let mut error = None; | ||||
| 
 | ||||
|         while { | ||||
|             self.skip_whitespace(); | ||||
|             if self.remaining() > 0 { | ||||
|                 match Expression::parse(&mut self) { | ||||
|                     Ok(exp) => { | ||||
|                         exp_list.push(exp); | ||||
|                         true | ||||
|                     } | ||||
|                     Err(err) => { | ||||
|                         error = Some(err); | ||||
|                         false | ||||
|                     } | ||||
|                 } | ||||
|             } else { | ||||
|                 false | ||||
|             } | ||||
|         } {} | ||||
| 
 | ||||
|         if let Some(error) = error { | ||||
|             Err(error) | ||||
|         } else { | ||||
|             Ok(ParsedReid(Expression::BlockExpr(Position(0, 0), exp_list))) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn remaining(&self) -> usize { | ||||
|         self.text.len() - self.cursor | ||||
|     } | ||||
| 
 | ||||
|     pub fn peek(&mut self, index: usize) -> Option<char> { | ||||
|         if self.remaining() < (index + 1) { | ||||
|             None | ||||
|         } else { | ||||
|             Some(self.text[self.cursor + index]) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn expect<T: Into<String>>(&mut self, expected: T) -> Expect { | ||||
|         let expected = expected.into(); | ||||
|         self.skip_whitespace(); | ||||
|         let mut result = Some(expected.clone()); | ||||
|         for (idx, c) in expected.chars().enumerate() { | ||||
|             if let Some(peek) = self.peek(idx) { | ||||
|                 if peek != c { | ||||
|                     result = None; | ||||
|                 } | ||||
|             } else { | ||||
|                 result = None; | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|         Expect { | ||||
|             text: result, | ||||
|             len: expected.len(), | ||||
|             parser: self, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn expect_ident(&mut self) -> Expect { | ||||
|         self.skip_whitespace(); | ||||
|         let mut ident: Option<String> = None; | ||||
|         let mut len = 0; | ||||
|         while { | ||||
|             if let Some(peek) = self.peek(len) { | ||||
|                 let lowercase = &peek.to_ascii_lowercase(); | ||||
|                 if let Some(id) = &mut ident { | ||||
|                     if ALLOWED_IDENT_CHARS.contains(lowercase) { | ||||
|                         id.push(peek); | ||||
|                         len += 1; | ||||
|                         true | ||||
|                     } else { | ||||
|                         false | ||||
|                     } | ||||
|                 } else { | ||||
|                     if ALLOWED_IDENT_BEGIN_CHARS.contains(lowercase) { | ||||
|                         ident = Some(peek.to_string()); | ||||
|                         len += 1; | ||||
|                         true | ||||
|                     } else { | ||||
|                         false | ||||
|                     } | ||||
|                 } | ||||
|             } else { | ||||
|                 false | ||||
|             } | ||||
|         } {} | ||||
|         Expect { | ||||
|             text: ident, | ||||
|             len: len, | ||||
|             parser: self, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn expect_string_lit(&mut self) -> Expect { | ||||
|         self.skip_whitespace(); | ||||
|         let mut content: Option<String> = None; | ||||
|         let mut len = 0; | ||||
|         while { | ||||
|             if let Some(peek) = self.peek(len) { | ||||
|                 if let Some(cont) = &mut content { | ||||
|                     if peek == '"' { | ||||
|                         len += 1; | ||||
|                         false | ||||
|                     } else { | ||||
|                         cont.push(peek); | ||||
|                         len += 1; | ||||
|                         true | ||||
|                     } | ||||
|                 } else { | ||||
|                     if peek == '"' { | ||||
|                         content = Some(String::new()); | ||||
|                         len += 1; | ||||
|                         true | ||||
|                     } else { | ||||
|                         false | ||||
|                     } | ||||
|                 } | ||||
|             } else { | ||||
|                 false | ||||
|             } | ||||
|         } {} | ||||
|         Expect { | ||||
|             text: content, | ||||
|             len: len, | ||||
|             parser: self, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn move_cursor(&mut self) { | ||||
|         let curr = self.peek(0).unwrap(); | ||||
|         self.cursor += 1; | ||||
|         self.character_number += 1; | ||||
|         if curr == '\n' { | ||||
|             self.character_number = 0; | ||||
|             self.line_number += 1; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn move_cursor_multiple(&mut self, amount: usize) { | ||||
|         for _ in 0..amount { | ||||
|             self.move_cursor(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn pos(&self) -> Position { | ||||
|         Position(self.line_number, self.character_number) | ||||
|     } | ||||
| 
 | ||||
|     fn skip_whitespace(&mut self) { | ||||
|         let mut next = ' '; | ||||
|         while self.remaining() > 0 && { | ||||
|             next = self.peek(0).unwrap(); | ||||
|             next == ' ' || next == '\n' || next == '\r' || next == '\t' | ||||
|         } { | ||||
|             self.move_cursor(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     fn empty() -> Parser { | ||||
|         Parser { | ||||
|             text: Vec::new(), | ||||
|             cursor: 0, | ||||
|             character_number: 0, | ||||
|             line_number: 0, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub struct ParsedReid(Expression); | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub enum Expression { | ||||
|     BlockExpr(Position, Vec<Expression>), | ||||
|     StatementExpr(Position, Statement), | ||||
|     EmptyExpr, | ||||
| } | ||||
| 
 | ||||
| impl Expression { | ||||
|     pub fn parse(parser: &mut Parser) -> Result<Expression, CompilerError> { | ||||
|         let begin_pos = parser.pos(); | ||||
| 
 | ||||
|         let expect = parser.expect("{"); | ||||
|         if let Some(_) = expect.get() { | ||||
|             let mut exp_list = Vec::new(); | ||||
|             while { | ||||
|                 match Expression::parse(parser) { | ||||
|                     Ok(exp) => { | ||||
|                         exp_list.push(exp); | ||||
|                         true | ||||
|                     } | ||||
|                     Err(_) => false, | ||||
|                 } | ||||
|             } {} | ||||
|             if let Some(_) = parser.expect("}").get() { | ||||
|                 Ok(Expression::BlockExpr(begin_pos, exp_list)) | ||||
|             } else { | ||||
|                 Err(CompilerError::ExpectedToken(parser.pos(), '}')) | ||||
|             } | ||||
|         } else { | ||||
|             match Statement::parse(parser) { | ||||
|                 Ok(statement) => Ok(Expression::StatementExpr(begin_pos, statement)), | ||||
|                 Err(err) => Err(CompilerError::ExpectedExpression(begin_pos, Box::new(err))), | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub enum Statement { | ||||
|     LetStatement(Position, Ident, Box<Expression>), | ||||
|     ValueRef(Position, Pattern), | ||||
| } | ||||
| 
 | ||||
| impl Statement { | ||||
|     pub fn parse(parser: &mut Parser) -> Result<Statement, CompilerError> { | ||||
|         let pos = parser.pos(); | ||||
| 
 | ||||
|         if let Some(_) = parser.expect("let").get() { | ||||
|             let ident = parser | ||||
|                 .expect_ident() | ||||
|                 .get_or(CompilerError::ExpectedIdent(pos))?; | ||||
|             parser | ||||
|                 .expect("=") | ||||
|                 .get_or(CompilerError::ExpectedToken(pos, '='))?; | ||||
|             match Expression::parse(parser) { | ||||
|                 Ok(expr) => { | ||||
|                     parser | ||||
|                         .expect(";") | ||||
|                         .get_or(CompilerError::ExpectedToken(pos, ';'))?; | ||||
|                     Ok(Statement::LetStatement(pos, ident, Box::new(expr))) | ||||
|                 } | ||||
|                 Err(err) => Err(CompilerError::ExpectedExpression(pos, Box::new(err))), | ||||
|             } | ||||
|         } else if let Ok(pattern) = Pattern::parse(parser) { | ||||
|             Ok(Statement::ValueRef(pos, pattern)) | ||||
|         } else { | ||||
|             Err(CompilerError::ExpectedStatement(pos)) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub enum Pattern { | ||||
|     IdentPattern(Position, Ident), | ||||
|     LiteralPattern(Position, LiteralPattern), | ||||
| } | ||||
| 
 | ||||
| impl Pattern { | ||||
|     fn parse(parser: &mut Parser) -> Result<Pattern, CompilerError> { | ||||
|         let pos = parser.pos(); | ||||
|         if let Some(string) = parser.expect_string_lit().get() { | ||||
|             Ok(Pattern::LiteralPattern( | ||||
|                 pos, | ||||
|                 LiteralPattern::StringLit(string), | ||||
|             )) | ||||
|         } else if let Some(ident) = parser.expect_ident().get() { | ||||
|             Ok(Pattern::IdentPattern(pos, ident)) | ||||
|         } else { | ||||
|             Err(CompilerError::ExpectedPattern(pos)) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub enum LiteralPattern { | ||||
|     StringLit(String), | ||||
| } | ||||
| 
 | ||||
| pub struct Expect<'a> { | ||||
|     text: Option<String>, | ||||
|     len: usize, | ||||
|     parser: &'a mut Parser, | ||||
| } | ||||
| 
 | ||||
| impl Expect<'_> { | ||||
|     pub fn get(mut self) -> Option<String> { | ||||
|         if self.text.is_some() { | ||||
|             self.parser.move_cursor_multiple(self.len); | ||||
|         } | ||||
|         self.text | ||||
|     } | ||||
|     pub fn get_or(mut self, error: CompilerError) -> Result<String, CompilerError> { | ||||
|         match self.get() { | ||||
|             Some(text) => Ok(text), | ||||
|             None => Err(error), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Copy, Clone)] | ||||
| pub struct Position(usize, usize); | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user