Compare commits
2 Commits
2df63504e0
...
698c914633
Author | SHA1 | Date |
---|---|---|
mars | 698c914633 | |
mars | 04c0dc35a6 |
30
src/lexer.rs
30
src/lexer.rs
|
@ -2,7 +2,7 @@ use logos::Logos;
|
|||
use std::fmt::{Display, Formatter, Result as FmtResult};
|
||||
|
||||
#[rustfmt::skip]
|
||||
#[derive(Logos, Debug, PartialEq)]
|
||||
#[derive(Logos, Clone, Copy, Debug, PartialEq)]
|
||||
pub enum Token {
|
||||
// keywords
|
||||
#[token("struct")] Struct,
|
||||
|
@ -78,10 +78,10 @@ impl<'a> Display for TokenInfo<'a> {
|
|||
|
||||
let token_len = self.token_end - self.token_start;
|
||||
if token_len > 1 {
|
||||
write!(f, "{}^", "-".repeat(token_len - 2))?;
|
||||
write!(f, "{}^\n", "-".repeat(token_len - 2))
|
||||
} else {
|
||||
write!(f, "\n")
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -113,7 +113,27 @@ impl<'a> Lexer<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get_info(&self) -> TokenInfo<'a> {
|
||||
pub fn eat_expect_id(&mut self) -> &'a str {
|
||||
let tok = self.next();
|
||||
if let Some(Token::Identifier) = tok {
|
||||
self.slice()
|
||||
} else {
|
||||
self.panic_message("Expected identifier");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eat_expect(&mut self, expected: Token) {
|
||||
let tok = self.next();
|
||||
if tok != Some(expected) {
|
||||
self.panic_message(&format!("Expected {:?}, got {:?}", expected, tok));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn panic_message(&self, message: &str) -> ! {
|
||||
panic!("{}\n{}", message, self.info());
|
||||
}
|
||||
|
||||
pub fn info(&self) -> TokenInfo<'a> {
|
||||
let from_start = &self.inner.source()[self.line_start..];
|
||||
let line_end = from_start.find("\n").unwrap_or(from_start.len());
|
||||
let line = &from_start[..line_end];
|
||||
|
|
27
src/lib.rs
27
src/lib.rs
|
@ -1,6 +1,6 @@
|
|||
use console::Style;
|
||||
|
||||
pub mod ast;
|
||||
pub mod parse;
|
||||
pub mod lexer;
|
||||
|
||||
use lexer::Token;
|
||||
|
@ -42,7 +42,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn lex_file() {
|
||||
let source = include_str!("ast_fn.fae");
|
||||
let source = include_str!("test/example.fae");
|
||||
let theme = ColorTheme::new();
|
||||
let mut lex = lexer::Lexer::new(source);
|
||||
|
||||
|
@ -56,7 +56,7 @@ mod tests {
|
|||
// TODO use spans to color-code instead of raw tokens, to show original whitespace
|
||||
/*#[test]
|
||||
fn color_file() {
|
||||
let source = include_str!("../example.fae");
|
||||
let source = include_str!("test/example.fae");
|
||||
let theme = ColorTheme::new();
|
||||
let mut lex = Token::lexer(source);
|
||||
|
||||
|
@ -65,25 +65,4 @@ mod tests {
|
|||
print!("{}", style.apply_to(lex.slice()));
|
||||
}
|
||||
}*/
|
||||
|
||||
fn print_ast(source: &str) {
|
||||
let mut lex = lexer::Lexer::new(source);
|
||||
let ast = ast::Ast::build(&mut lex);
|
||||
println!("{:#?}", ast);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ast_fn() {
|
||||
print_ast(include_str!("ast_fn.fae"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ast_struct() {
|
||||
print_ast(include_str!("ast_struct.fae"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ast_example() {
|
||||
print_ast(include_str!("../example.fae"));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +1,20 @@
|
|||
use crate::lexer::{Lexer, Token};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Ast<'a> {
|
||||
pub struct ParseTree<'a> {
|
||||
pub declarations: Vec<Definition<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> Ast<'a> {
|
||||
impl<'a> ParseTree<'a> {
|
||||
pub fn build(lexer: &mut Lexer<'a>) -> Self {
|
||||
let mut declarations = Vec::new();
|
||||
let mut associated_struct = None;
|
||||
while let Some(tok) = lexer.next() {
|
||||
if let Some(_) = associated_struct {
|
||||
assert_eq!(Token::Function, tok);
|
||||
if tok != Token::Function {
|
||||
lexer.panic_message("Expected fn");
|
||||
}
|
||||
|
||||
declarations.push(Definition::build_function(associated_struct, lexer));
|
||||
associated_struct = None;
|
||||
} else {
|
||||
|
@ -19,7 +22,7 @@ impl<'a> Ast<'a> {
|
|||
Token::Struct => declarations.push(Definition::build_structure(lexer)),
|
||||
Token::Function => declarations.push(Definition::build_function(None, lexer)),
|
||||
Token::Identifier => associated_struct = Some(lexer.slice()),
|
||||
_ => panic!("Expected associated struct identifier, fn, or struct"),
|
||||
_ => lexer.panic_message("Expected associated struct identifier, fn or struct"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -44,7 +47,7 @@ pub enum Definition<'a> {
|
|||
impl<'a> Definition<'a> {
|
||||
pub fn build_structure(lexer: &mut Lexer<'a>) -> Self {
|
||||
let name = lexer.eat_id().unwrap();
|
||||
assert_eq!(lexer.next(), Some(Token::BraceOpen));
|
||||
lexer.eat_expect(Token::BraceOpen);
|
||||
|
||||
let mut members = Vec::new();
|
||||
|
||||
|
@ -59,7 +62,7 @@ impl<'a> Definition<'a> {
|
|||
}
|
||||
Err(Some(Token::Comma)) => {}
|
||||
Err(Some(Token::BraceClose)) => break,
|
||||
_ => panic!("Expected comma or closing brace"),
|
||||
_ => lexer.panic_message("Expected comma or closing brace"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -67,9 +70,9 @@ impl<'a> Definition<'a> {
|
|||
}
|
||||
|
||||
pub fn build_function(associated_struct: Option<&'a str>, lexer: &mut Lexer<'a>) -> Self {
|
||||
let name = lexer.eat_id().unwrap();
|
||||
let name = lexer.eat_expect_id();
|
||||
lexer.eat_expect(Token::ParanOpen);
|
||||
|
||||
assert_eq!(lexer.next(), Some(Token::ParanOpen));
|
||||
let mut args = Vec::new();
|
||||
|
||||
loop {
|
||||
|
@ -83,7 +86,7 @@ impl<'a> Definition<'a> {
|
|||
}
|
||||
Err(Some(Token::Comma)) => {}
|
||||
Err(Some(Token::ParanClose)) => break,
|
||||
_ => panic!("Expected comma, type, or closing paranthases"),
|
||||
_ => lexer.panic_message("Expected comma, type, or closing parantheses"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -91,9 +94,10 @@ impl<'a> Definition<'a> {
|
|||
let mut return_type = None;
|
||||
if let Some(Token::Identifier) = tok {
|
||||
return_type = Some(lexer.slice());
|
||||
assert_eq!(Some(Token::BraceOpen), lexer.next());
|
||||
} else {
|
||||
assert_eq!(Some(Token::BraceOpen), tok);
|
||||
lexer.eat_expect(Token::BraceOpen);
|
||||
} else if tok != Some(Token::BraceOpen) {
|
||||
let info = lexer.info();
|
||||
panic!("Expected open bracket:\n{}", info);
|
||||
}
|
||||
|
||||
let signature = FunctionSignature {
|
||||
|
@ -127,7 +131,7 @@ pub struct FunctionSignature<'a> {
|
|||
|
||||
impl<'a> FunctionSignature<'a> {
|
||||
pub fn build(associated_struct: Option<&'a str>, lexer: &mut Lexer<'a>) -> Self {
|
||||
assert_eq!(lexer.next(), Some(Token::ParanOpen));
|
||||
lexer.eat_expect(Token::ParanOpen);
|
||||
let mut args = Vec::new();
|
||||
|
||||
loop {
|
||||
|
@ -141,7 +145,7 @@ impl<'a> FunctionSignature<'a> {
|
|||
}
|
||||
Err(Some(Token::Comma)) => {}
|
||||
Err(Some(Token::ParanClose)) => break,
|
||||
_ => panic!("Expected comma, type, or closing paranthases"),
|
||||
_ => lexer.panic_message("Expected comma, type, or closing parantheses"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -221,3 +225,29 @@ pub struct BranchBody<'a> {
|
|||
pub statements: Vec<Statement<'a>>,
|
||||
pub tail_expression: Box<Expression<'a>>,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn parse(source: &str) {
|
||||
let mut lex = Lexer::new(source);
|
||||
let parse_tree = ParseTree::build(&mut lex);
|
||||
println!("{:#?}", parse_tree);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function() {
|
||||
parse(include_str!("test/function.fae"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn structure() {
|
||||
parse(include_str!("test/structure.fae"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn example() {
|
||||
parse(include_str!("test/example.fae"));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue