updated compiler to support multiple frontends and backends
This commit is contained in:
@@ -0,0 +1,627 @@
|
||||
use std::iter::Peekable;
|
||||
use std::str::Chars;
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum Token {
|
||||
// Keywords
|
||||
Fn,
|
||||
Let,
|
||||
If,
|
||||
Else,
|
||||
Loop,
|
||||
While,
|
||||
Break,
|
||||
Return,
|
||||
Continue,
|
||||
Include,
|
||||
Static,
|
||||
Const,
|
||||
|
||||
// Identifiers and literals
|
||||
Identifier(Name),
|
||||
String(String),
|
||||
Integer(u64),
|
||||
Char(char),
|
||||
|
||||
// Symbols
|
||||
LeftParen, // (
|
||||
RightParen, // )
|
||||
LeftBrace, // {
|
||||
RightBrace, // }
|
||||
Semicolon, // ;
|
||||
Colon, // :
|
||||
Comma, // ,
|
||||
|
||||
// Operators
|
||||
Plus, // +
|
||||
Minus, // -
|
||||
Star, // *
|
||||
Amphersand, // &
|
||||
Slash, // /
|
||||
Assign, // =
|
||||
EqualEqual, // ==
|
||||
Bang, // !
|
||||
BangEqual, // !=
|
||||
Less, // <
|
||||
LessEqual, // <=
|
||||
Greater, // >
|
||||
GreaterEqual, // >=
|
||||
RightArrow, // ->
|
||||
|
||||
// Special
|
||||
Eof,
|
||||
}
|
||||
|
||||
use std::fmt;
|
||||
|
||||
use crate::model::Name;
|
||||
|
||||
impl fmt::Display for Name {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if let Some(ref ns) = self.namespace {
|
||||
write!(f, "{}::{}", ns, self.name)
|
||||
} else {
|
||||
write!(f, "{}", self.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Token {
|
||||
pub fn tt(&self) -> &str {
|
||||
match self {
|
||||
Token::Const => "Const",
|
||||
Token::Static => "Static",
|
||||
Token::Include => "Include",
|
||||
Token::Fn => "Fn",
|
||||
Token::If => "If",
|
||||
Token::Let => "Let",
|
||||
Token::Else => "Else",
|
||||
Token::Loop => "Loop",
|
||||
Token::While => "While",
|
||||
Token::Break => "Break",
|
||||
Token::Return => "Return",
|
||||
Token::Continue => "Continue",
|
||||
Token::Identifier(_) => "Identifier",
|
||||
Token::String(_) => "String",
|
||||
Token::Integer(_) => "UnsignedInt",
|
||||
Token::Char(_) => "Char",
|
||||
Token::LeftParen => "LeftParen",
|
||||
Token::RightParen => "RightParen",
|
||||
Token::LeftBrace => "LeftBrace",
|
||||
Token::RightBrace => "RightBrace",
|
||||
Token::Semicolon => "Semicolon",
|
||||
Token::Colon => "Colon",
|
||||
Token::Comma => "Comma",
|
||||
Token::RightArrow => "RightArrow",
|
||||
Token::Plus => "Plus",
|
||||
Token::Minus => "Minus",
|
||||
Token::Star => "Star",
|
||||
Token::Amphersand => "Amphersand",
|
||||
Token::Slash => "Slash",
|
||||
Token::Assign => "Assign",
|
||||
Token::EqualEqual => "EqualEqual",
|
||||
Token::Bang => "Bang",
|
||||
Token::BangEqual => "BangEqual",
|
||||
Token::Less => "Less",
|
||||
Token::LessEqual => "LessEqual",
|
||||
Token::Greater => "Greater",
|
||||
Token::GreaterEqual => "GreaterEqual",
|
||||
Token::Eof => "Eof",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Lexer<'a> {
|
||||
chars: Peekable<Chars<'a>>,
|
||||
current: Option<char>,
|
||||
line: usize,
|
||||
}
|
||||
|
||||
impl<'a> Lexer<'a> {
|
||||
pub fn new(input: &'a str) -> Self {
|
||||
let mut chars = input.chars().peekable();
|
||||
let current = chars.next();
|
||||
|
||||
Lexer {
|
||||
chars,
|
||||
current,
|
||||
line: 1,
|
||||
}
|
||||
}
|
||||
|
||||
fn advance(&mut self) -> Option<char> {
|
||||
self.current = self.chars.next();
|
||||
self.current
|
||||
}
|
||||
|
||||
fn peek(&mut self) -> Option<&char> {
|
||||
self.chars.peek()
|
||||
}
|
||||
|
||||
fn skip_whitespace(&mut self) {
|
||||
while let Some(c) = self.current {
|
||||
if !c.is_whitespace() {
|
||||
break;
|
||||
}
|
||||
if c == '\n' {
|
||||
self.line += 1;
|
||||
}
|
||||
self.advance();
|
||||
}
|
||||
}
|
||||
|
||||
fn skip_line_comment(&mut self) {
|
||||
// Skip the two slashes
|
||||
self.advance(); // first /
|
||||
self.advance(); // second /
|
||||
|
||||
// Skip until newline or EOF
|
||||
while let Some(c) = self.current {
|
||||
if c == '\n' {
|
||||
self.line += 1;
|
||||
self.advance();
|
||||
break;
|
||||
}
|
||||
self.advance();
|
||||
}
|
||||
}
|
||||
|
||||
fn skip_block_comment(&mut self) -> Result<(), String> {
|
||||
// Skip the /*
|
||||
self.advance(); // /
|
||||
self.advance(); // *
|
||||
|
||||
let start_line = self.line;
|
||||
|
||||
// Look for */
|
||||
while let Some(c) = self.current {
|
||||
if c == '\n' {
|
||||
self.line += 1;
|
||||
}
|
||||
|
||||
if c == '*' {
|
||||
if let Some(&next) = self.peek() {
|
||||
if next == '/' {
|
||||
self.advance(); // *
|
||||
self.advance(); // /
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.advance();
|
||||
}
|
||||
|
||||
Err(format!(
|
||||
"Unterminated block comment starting at line {}",
|
||||
start_line
|
||||
))
|
||||
}
|
||||
|
||||
fn skip_whitespace_and_comments(&mut self) {
|
||||
loop {
|
||||
self.skip_whitespace();
|
||||
|
||||
// Check for comments
|
||||
if let Some('/') = self.current {
|
||||
if let Some(&next) = self.peek() {
|
||||
match next {
|
||||
'/' => {
|
||||
self.skip_line_comment();
|
||||
continue;
|
||||
}
|
||||
'*' => {
|
||||
if let Err(e) = self.skip_block_comment() {
|
||||
eprintln!("Lexer error: {}", e);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fn read_identifier(&mut self) -> String {
|
||||
let mut ident = String::new();
|
||||
|
||||
// Include the current character if it's valid
|
||||
if let Some(c) = self.current {
|
||||
if c.is_alphabetic() || c == '_' {
|
||||
ident.push(c);
|
||||
}
|
||||
}
|
||||
|
||||
// Read remaining characters
|
||||
while let Some(&c) = self.peek() {
|
||||
if c.is_alphanumeric() || c == '_' {
|
||||
self.advance();
|
||||
ident.push(c);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ident
|
||||
}
|
||||
|
||||
fn keyword_or_identifier(&mut self) -> Token {
|
||||
let first_ident = self.read_identifier();
|
||||
|
||||
// Check if it's a keyword first (keywords can't have namespaces)
|
||||
let keyword = match first_ident.as_str() {
|
||||
"fn" => Some(Token::Fn),
|
||||
"if" => Some(Token::If),
|
||||
"else" => Some(Token::Else),
|
||||
"while" => Some(Token::While),
|
||||
"loop" => Some(Token::Loop),
|
||||
"break" => Some(Token::Break),
|
||||
"return" => Some(Token::Return),
|
||||
"continue" => Some(Token::Continue),
|
||||
"include" => Some(Token::Include),
|
||||
"let" => Some(Token::Let),
|
||||
"const" => Some(Token::Const),
|
||||
"static" => Some(Token::Static),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
if let Some(kw) = keyword {
|
||||
return kw;
|
||||
}
|
||||
|
||||
// Not a keyword - check for namespace separator (::)
|
||||
// We need to peek TWO characters ahead without consuming anything
|
||||
if let Some(&':') = self.peek() {
|
||||
// We see one colon, but we need to check if there's another one after it
|
||||
// We can't peek two ahead directly, so we need a different approach
|
||||
|
||||
// Save the current position by using a temporary peekable iterator
|
||||
// Actually, we can't do that easily. Instead, let's just check:
|
||||
// If we see ':', temporarily advance and check the next char
|
||||
|
||||
// Create a temporary check
|
||||
let mut temp_chars = self.chars.clone();
|
||||
let _ = temp_chars.next(); // This is the ':' we already saw
|
||||
let second_peek = temp_chars.peek();
|
||||
|
||||
if let Some(&':') = second_peek {
|
||||
// It's :: - consume both colons
|
||||
self.advance(); // consume first :
|
||||
self.advance(); // consume second :
|
||||
|
||||
// Read the second identifier (the actual name)
|
||||
let second_ident = self.read_identifier();
|
||||
|
||||
// Return namespaced identifier
|
||||
return Token::Identifier(Name {
|
||||
namespace: Some(first_ident),
|
||||
name: second_ident,
|
||||
});
|
||||
}
|
||||
// else: It's a single colon (type annotation) - DON'T consume it
|
||||
// Just fall through and return the identifier
|
||||
}
|
||||
|
||||
// No namespace separator - just a regular identifier
|
||||
Token::Identifier(Name {
|
||||
namespace: None,
|
||||
name: first_ident,
|
||||
})
|
||||
}
|
||||
|
||||
fn read_number(&mut self) -> Result<u64, String> {
|
||||
let current = self.current.unwrap();
|
||||
|
||||
// Check for hex (0x) or binary (0b) prefix
|
||||
if current == '0' {
|
||||
if let Some(&next_char) = self.peek() {
|
||||
match next_char {
|
||||
'x' | 'X' => {
|
||||
self.advance(); // consume '0'
|
||||
self.advance(); // consume 'x'
|
||||
return self.read_hex_number();
|
||||
}
|
||||
'b' | 'B' => {
|
||||
self.advance(); // consume '0'
|
||||
self.advance(); // consume 'b'
|
||||
return self.read_binary_number();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Read decimal number
|
||||
self.read_decimal_number()
|
||||
}
|
||||
|
||||
fn read_decimal_number(&mut self) -> Result<u64, String> {
|
||||
let mut num_str = String::new();
|
||||
|
||||
if let Some(c) = self.current {
|
||||
num_str.push(c);
|
||||
}
|
||||
|
||||
while let Some(&c) = self.peek() {
|
||||
if c.is_ascii_digit() {
|
||||
self.advance();
|
||||
num_str.push(c);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
num_str
|
||||
.parse::<u64>()
|
||||
.map_err(|_| format!("Invalid decimal number: {}", num_str))
|
||||
}
|
||||
|
||||
fn read_hex_number(&mut self) -> Result<u64, String> {
|
||||
let mut num_str = String::new();
|
||||
|
||||
// Read current character if it's a hex digit
|
||||
if let Some(c) = self.current {
|
||||
if c.is_ascii_hexdigit() {
|
||||
num_str.push(c);
|
||||
}
|
||||
}
|
||||
|
||||
while let Some(&c) = self.peek() {
|
||||
if c.is_ascii_hexdigit() {
|
||||
self.advance();
|
||||
num_str.push(c);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if num_str.is_empty() {
|
||||
return Err("Invalid hexadecimal number: no digits after 0x".to_string());
|
||||
}
|
||||
|
||||
u64::from_str_radix(&num_str, 16)
|
||||
.map_err(|_| format!("Invalid hexadecimal number: {}", num_str))
|
||||
}
|
||||
|
||||
fn read_binary_number(&mut self) -> Result<u64, String> {
|
||||
let mut num_str = String::new();
|
||||
|
||||
// Read current character if it's a binary digit
|
||||
if let Some(c) = self.current {
|
||||
if c == '0' || c == '1' {
|
||||
num_str.push(c);
|
||||
}
|
||||
}
|
||||
|
||||
while let Some(&c) = self.peek() {
|
||||
if c == '0' || c == '1' {
|
||||
self.advance();
|
||||
num_str.push(c);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if num_str.is_empty() {
|
||||
return Err("Invalid binary number: no digits after 0b".to_string());
|
||||
}
|
||||
|
||||
u64::from_str_radix(&num_str, 2)
|
||||
.map_err(|_| format!("Invalid binary number: {}", num_str))
|
||||
}
|
||||
|
||||
fn read_string(&mut self) -> Result<String, String> {
|
||||
self.advance(); // Skip the opening quote
|
||||
let mut s = String::new();
|
||||
|
||||
while let Some(c) = self.current {
|
||||
if c == '"' {
|
||||
return Ok(s);
|
||||
}
|
||||
|
||||
// Handle escape sequences
|
||||
if c == '\\' {
|
||||
self.advance();
|
||||
if let Some(escaped) = self.current {
|
||||
let escaped_char = match escaped {
|
||||
'n' => '\n',
|
||||
't' => '\t',
|
||||
'r' => '\r',
|
||||
'\\' => '\\',
|
||||
'"' => '"',
|
||||
_ => escaped, // For now, just use the character as-is
|
||||
};
|
||||
s.push(escaped_char);
|
||||
} else {
|
||||
return Err("Unexpected end of string after escape".to_string());
|
||||
}
|
||||
} else {
|
||||
s.push(c);
|
||||
}
|
||||
|
||||
self.advance();
|
||||
}
|
||||
|
||||
Err("Unterminated string literal".to_string())
|
||||
}
|
||||
|
||||
fn match_next(&mut self, expected: char) -> bool {
|
||||
match self.peek() {
|
||||
Some(&c) if c == expected => {
|
||||
self.advance();
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn scan_single_char_token(&mut self, c: char) -> Option<Token> {
|
||||
match c {
|
||||
'(' => Some(Token::LeftParen),
|
||||
')' => Some(Token::RightParen),
|
||||
'{' => Some(Token::LeftBrace),
|
||||
'}' => Some(Token::RightBrace),
|
||||
';' => Some(Token::Semicolon),
|
||||
',' => Some(Token::Comma),
|
||||
'&' => Some(Token::Amphersand),
|
||||
'+' => Some(Token::Plus),
|
||||
'*' => Some(Token::Star),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn scan_operator(&mut self, c: char) -> Option<Token> {
|
||||
match c {
|
||||
'-' => Some(if self.match_next('>') {
|
||||
Token::RightArrow
|
||||
} else {
|
||||
Token::Minus
|
||||
}),
|
||||
'!' => Some(if self.match_next('=') {
|
||||
Token::BangEqual
|
||||
} else {
|
||||
Token::Bang
|
||||
}),
|
||||
'=' => Some(if self.match_next('=') {
|
||||
Token::EqualEqual
|
||||
} else {
|
||||
Token::Assign
|
||||
}),
|
||||
'<' => Some(if self.match_next('=') {
|
||||
Token::LessEqual
|
||||
} else {
|
||||
Token::Less
|
||||
}),
|
||||
'>' => Some(if self.match_next('=') {
|
||||
Token::GreaterEqual
|
||||
} else {
|
||||
Token::Greater
|
||||
}),
|
||||
':' => {
|
||||
// Single colon (for type annotations)
|
||||
// Note: :: is handled in keyword_or_identifier for namespaces
|
||||
Some(Token::Colon)
|
||||
}
|
||||
'/' => {
|
||||
// Check if it's a comment or division
|
||||
if let Some(&next) = self.peek() {
|
||||
if next == '/' || next == '*' {
|
||||
// It's a comment, don't consume it here
|
||||
// Let skip_whitespace_and_comments handle it
|
||||
None
|
||||
} else {
|
||||
Some(Token::Slash)
|
||||
}
|
||||
} else {
|
||||
Some(Token::Slash)
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn next_token(&mut self) -> Token {
|
||||
self.skip_whitespace_and_comments();
|
||||
|
||||
let Some(c) = self.current else {
|
||||
return Token::Eof;
|
||||
};
|
||||
|
||||
// Try single-character tokens first
|
||||
if let Some(token) = self.scan_single_char_token(c) {
|
||||
self.advance();
|
||||
return token;
|
||||
}
|
||||
|
||||
// Try operators (may be multi-character)
|
||||
if let Some(token) = self.scan_operator(c) {
|
||||
self.advance();
|
||||
return token;
|
||||
}
|
||||
|
||||
// Char literals
|
||||
if c == '\'' {
|
||||
let mut value = ' ';
|
||||
self.advance();
|
||||
if let Some(ch) = self.current {
|
||||
value = ch;
|
||||
self.advance();
|
||||
}
|
||||
if self.current == Some('\'') {
|
||||
self.advance();
|
||||
return Token::Char(value);
|
||||
}
|
||||
eprintln!("Lexer error on line {}: Invalid char literal", self.line);
|
||||
}
|
||||
|
||||
// String literals
|
||||
if c == '"' {
|
||||
let token = match self.read_string() {
|
||||
Ok(s) => Token::String(s),
|
||||
Err(e) => {
|
||||
eprintln!("Lexer error on line {}: {}", self.line, e);
|
||||
// Skip to next quote or end
|
||||
while let Some(ch) = self.current {
|
||||
if ch == '"' || ch == '\n' {
|
||||
break;
|
||||
}
|
||||
self.advance();
|
||||
}
|
||||
Token::String(String::new())
|
||||
}
|
||||
};
|
||||
self.advance();
|
||||
return token;
|
||||
}
|
||||
|
||||
// Identifiers and keywords (including namespaced identifiers)
|
||||
if c.is_alphabetic() || c == '_' {
|
||||
let token = self.keyword_or_identifier();
|
||||
self.advance();
|
||||
return token;
|
||||
}
|
||||
|
||||
// Numbers (decimal, hex, binary)
|
||||
if c.is_ascii_digit() {
|
||||
let token = match self.read_number() {
|
||||
Ok(num) => Token::Integer(num),
|
||||
Err(e) => {
|
||||
eprintln!("Lexer error on line {}: {}", self.line, e);
|
||||
// Skip invalid number
|
||||
while let Some(&ch) = self.peek() {
|
||||
if !ch.is_alphanumeric() {
|
||||
break;
|
||||
}
|
||||
self.advance();
|
||||
}
|
||||
Token::Integer(0)
|
||||
}
|
||||
};
|
||||
self.advance();
|
||||
return token;
|
||||
}
|
||||
|
||||
// Unknown character - skip it
|
||||
eprintln!(
|
||||
"Lexer warning on line {}: Skipping unknown character '{}'",
|
||||
self.line, c
|
||||
);
|
||||
self.advance();
|
||||
self.next_token()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for Lexer<'a> {
|
||||
type Item = Token;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match self.next_token() {
|
||||
Token::Eof => None,
|
||||
token => Some(token),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
use common::logging::log;
|
||||
|
||||
use crate::model::{CompilerError, Program};
|
||||
use parser::{ParseResult, Parser};
|
||||
use semantic_analyser::Analyser;
|
||||
|
||||
pub mod lexer;
|
||||
pub mod parser;
|
||||
pub mod semantic_analyser;
|
||||
|
||||
pub fn generate_ast(input: &str) -> Result<Program, CompilerError> {
|
||||
log("Tokenising Input...");
|
||||
|
||||
let lexer = lexer::Lexer::new(&input);
|
||||
let tokens = lexer.collect::<Vec<_>>();
|
||||
// println!("{tokens:?}");
|
||||
|
||||
log(&format!("Parsing {} Tokens...", tokens.len()));
|
||||
|
||||
let mut parser = Parser::new(tokens);
|
||||
let ast = match parser.parse() {
|
||||
ParseResult::Accept(ast) => ast,
|
||||
ParseResult::Reject(e) => return Err(e),
|
||||
ParseResult::Deny => {
|
||||
return Err(CompilerError::Generic("Parser used ::Deny".to_string()));
|
||||
}
|
||||
};
|
||||
// println!("{ast:#?}");
|
||||
|
||||
log("Analyzing AST...");
|
||||
log("Checking Type Information...");
|
||||
|
||||
let analyser = Analyser::new();
|
||||
analyser.analyse(ast.clone()).unwrap();
|
||||
|
||||
log("Type Checking Complete...");
|
||||
Ok(ast)
|
||||
}
|
||||
@@ -0,0 +1,604 @@
|
||||
use super::lexer::Token;
|
||||
use crate::model::{
|
||||
BinaryOperator, Block, CompilerError, ConstExpr, Declaration, Dependency, Expression,
|
||||
Program, Statement, TypeId, UnaryOperator, Variable,
|
||||
};
|
||||
use crate::{expect_tt, expect_value};
|
||||
use std::ops::{ControlFlow, FromResidual, Try};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ParseResult<T, E> {
|
||||
Accept(T),
|
||||
Deny,
|
||||
Reject(E),
|
||||
}
|
||||
|
||||
pub struct Parser {
|
||||
tokens: Vec<Token>,
|
||||
idx: usize,
|
||||
}
|
||||
|
||||
impl Parser {
|
||||
pub fn new(tokens: Vec<Token>) -> Self {
|
||||
Self { tokens, idx: 0 }
|
||||
}
|
||||
|
||||
pub fn parse(&mut self) -> ParseResult<Program, CompilerError> {
|
||||
let mut declarations = Vec::new();
|
||||
|
||||
while let ParseResult::Accept(_) = self.peek_next() {
|
||||
declarations.push(self.parse_declaration()?);
|
||||
}
|
||||
|
||||
ParseResult::Accept(Program { declarations })
|
||||
}
|
||||
|
||||
fn parse_declaration(&mut self) -> ParseResult<Declaration, CompilerError> {
|
||||
if expect_tt!(self.peek_next()?, Fn).accepted() {
|
||||
return self.parse_func();
|
||||
}
|
||||
|
||||
if expect_tt!(self.peek_next()?, Include).accepted() {
|
||||
// expect include keyword
|
||||
let _ = self.next();
|
||||
|
||||
// expect namespace identifier
|
||||
let name = expect_value!(self.next()?, Identifier)?;
|
||||
|
||||
// expect colon
|
||||
let _ = expect_tt!(self.next()?, Colon)?;
|
||||
|
||||
// expect string literal (module path)
|
||||
let path = expect_value!(self.next()?, String)?;
|
||||
|
||||
// expect semicolon
|
||||
let _ = expect_tt!(self.next()?, Semicolon)?;
|
||||
|
||||
return ParseResult::Accept(Declaration::Dependency(Dependency {
|
||||
name: name.name,
|
||||
path,
|
||||
}));
|
||||
}
|
||||
|
||||
if expect_tt!(self.peek_next()?, Const, Static).accepted() {
|
||||
let is_const = match self.next()? {
|
||||
Token::Const => true,
|
||||
Token::Static => false,
|
||||
_ => {
|
||||
return ParseResult::Reject(CompilerError::Generic(String::from(
|
||||
"This can't happen!",
|
||||
)));
|
||||
}
|
||||
};
|
||||
|
||||
let var = self.parse_var_decl()?;
|
||||
|
||||
let _ = expect_tt!(self.next()?, Assign)?;
|
||||
|
||||
let value = self.next()?;
|
||||
let init = match value {
|
||||
Token::String(x) => Some(ConstExpr::String(x)),
|
||||
Token::Integer(x) => Some(ConstExpr::Number(x as i32)),
|
||||
_ => {
|
||||
return ParseResult::Reject(CompilerError::UnexpectedToken(
|
||||
value.tt().to_string(),
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
let _ = expect_tt!(self.next()?, Semicolon)?;
|
||||
|
||||
return ParseResult::Accept(Declaration::Variable {
|
||||
var,
|
||||
init,
|
||||
is_const,
|
||||
});
|
||||
}
|
||||
|
||||
ParseResult::Reject(CompilerError::UnexpectedEndOfInput)
|
||||
}
|
||||
|
||||
fn parse_func(&mut self) -> ParseResult<Declaration, CompilerError> {
|
||||
// expect function keyword
|
||||
let _ = expect_tt!(self.next()?, Fn);
|
||||
// expect function name
|
||||
let name = expect_value!(self.next()?, Identifier)?;
|
||||
|
||||
// expect left paren
|
||||
let _ = expect_tt!(self.next()?, LeftParen)?;
|
||||
|
||||
let mut params = Vec::new();
|
||||
while expect_tt!(self.peek_next()?, Identifier).accepted() {
|
||||
let arg = self.parse_var_decl()?;
|
||||
params.push(arg);
|
||||
|
||||
if expect_tt!(self.peek_next()?, Comma).accepted() {
|
||||
self.next()?;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// expect right paren
|
||||
let _ = expect_tt!(self.next()?, RightParen)?;
|
||||
|
||||
// see if we can parse the return type!
|
||||
let mut return_type = TypeId::Void;
|
||||
if expect_tt!(self.peek_next()?, RightArrow).accepted() {
|
||||
let _ = self.next();
|
||||
return_type = self.parse_type()?;
|
||||
}
|
||||
|
||||
// expect vald block
|
||||
if expect_tt!(self.peek_next()?, LeftBrace).accepted() {
|
||||
ParseResult::Accept(Declaration::Function {
|
||||
name: name.name,
|
||||
params,
|
||||
return_type,
|
||||
body: self.parse_block()?,
|
||||
})
|
||||
} else {
|
||||
ParseResult::Reject(CompilerError::UnexpectedToken(
|
||||
self.peek_next()?.tt().to_string(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_block(&mut self) -> ParseResult<Block, CompilerError> {
|
||||
// expect left brace
|
||||
let _ = expect_tt!(self.next()?, LeftBrace)?;
|
||||
|
||||
let mut block = Vec::new();
|
||||
while !expect_tt!(self.peek_next()?, RightBrace).accepted() {
|
||||
block.push(self.parse_statement()?);
|
||||
}
|
||||
|
||||
// expect right brace
|
||||
let _ = expect_tt!(self.next()?, RightBrace)?;
|
||||
|
||||
ParseResult::Accept(block)
|
||||
}
|
||||
|
||||
fn parse_statement(&mut self) -> ParseResult<Statement, CompilerError> {
|
||||
// handle if statements
|
||||
if expect_tt!(self.peek_next()?, If).accepted() {
|
||||
self.next()?;
|
||||
|
||||
let condition = self.parse_expression()?;
|
||||
|
||||
let then_stmt = self.parse_block()?;
|
||||
|
||||
if !expect_tt!(self.peek_next()?, Else).accepted() {
|
||||
return ParseResult::Accept(Statement::If {
|
||||
condition,
|
||||
then_stmt,
|
||||
else_stmt: vec![],
|
||||
});
|
||||
}
|
||||
|
||||
let _ = expect_tt!(self.next()?, Else)?;
|
||||
|
||||
let else_stmt = self.parse_block()?;
|
||||
|
||||
return ParseResult::Accept(Statement::If {
|
||||
condition,
|
||||
then_stmt,
|
||||
else_stmt,
|
||||
});
|
||||
}
|
||||
|
||||
// handle while loops
|
||||
if expect_tt!(self.peek_next()?, While).accepted() {
|
||||
self.next()?;
|
||||
|
||||
// expect valid expression
|
||||
let expr = self.parse_expression()?;
|
||||
|
||||
// expect valid block after
|
||||
let block = self.parse_block()?;
|
||||
|
||||
// return result
|
||||
return ParseResult::Accept(Statement::While {
|
||||
condition: expr,
|
||||
body: block,
|
||||
});
|
||||
}
|
||||
|
||||
// handle indefinite loops
|
||||
if expect_tt!(self.peek_next()?, Loop).accepted() {
|
||||
self.next()?;
|
||||
|
||||
// parse the inner block
|
||||
return ParseResult::Accept(Statement::Loop(self.parse_block()?));
|
||||
}
|
||||
|
||||
if expect_tt!(self.peek_next()?, Return).accepted() {
|
||||
self.next()?;
|
||||
|
||||
// handle case where nothing is returned
|
||||
if expect_tt!(self.peek_next()?, Semicolon).accepted() {
|
||||
return ParseResult::Accept(Statement::Return(None));
|
||||
}
|
||||
|
||||
let expr = self.parse_expression()?;
|
||||
expect_tt!(self.next()?, Semicolon)?;
|
||||
|
||||
return ParseResult::Accept(Statement::Return(Some(expr)));
|
||||
}
|
||||
|
||||
if expect_tt!(self.peek_next()?, Break).accepted() {
|
||||
self.next()?;
|
||||
|
||||
// expect semicolon
|
||||
expect_tt!(self.next()?, Semicolon)?;
|
||||
|
||||
// return result
|
||||
return ParseResult::Accept(Statement::Break);
|
||||
}
|
||||
|
||||
if expect_tt!(self.peek_next()?, Continue).accepted() {
|
||||
self.next()?;
|
||||
|
||||
// expect semicolon
|
||||
expect_tt!(self.next()?, Semicolon)?;
|
||||
|
||||
// return result
|
||||
return ParseResult::Accept(Statement::Continue);
|
||||
}
|
||||
|
||||
// handle writes to pointers!
|
||||
if expect_tt!(self.peek_next()?, Star).accepted() {
|
||||
self.next()?;
|
||||
|
||||
let left = if expect_tt!(self.peek_next()?, Identifier).accepted() {
|
||||
let identifier = expect_value!(self.next()?, Identifier)?;
|
||||
|
||||
Expression::Variable {
|
||||
name: identifier,
|
||||
expr_type: None,
|
||||
}
|
||||
} else if expect_tt!(self.peek_next()?, LeftParen).accepted() {
|
||||
self.next()?;
|
||||
|
||||
let expr = self.parse_expression()?;
|
||||
|
||||
let _ = expect_tt!(self.next()?, RightParen).accepted();
|
||||
|
||||
expr
|
||||
} else {
|
||||
return ParseResult::Reject(CompilerError::UnexpectedToken(
|
||||
self.peek_next()?.tt().to_string(),
|
||||
));
|
||||
};
|
||||
|
||||
let _ = expect_tt!(self.next()?, Assign)?;
|
||||
|
||||
let right = self.parse_expression()?;
|
||||
|
||||
// expect semicolon
|
||||
expect_tt!(self.next()?, Semicolon)?;
|
||||
|
||||
// return result
|
||||
return ParseResult::Accept(Statement::PtrWrite {
|
||||
ptr: left,
|
||||
value: right,
|
||||
});
|
||||
}
|
||||
|
||||
// handle let statements (declarations)
|
||||
if expect_tt!(self.peek_next()?, Let).accepted() {
|
||||
self.next();
|
||||
|
||||
// expect variable name and type.
|
||||
let name = self.parse_var_decl()?;
|
||||
|
||||
// handle uninitialised variable case
|
||||
if expect_tt!(self.peek_next()?, Semicolon).accepted() {
|
||||
self.next();
|
||||
return ParseResult::Accept(Statement::Declaration {
|
||||
var: name,
|
||||
value: None,
|
||||
});
|
||||
}
|
||||
|
||||
// handle initialised case
|
||||
// expect equals
|
||||
let _ = expect_tt!(self.next()?, Assign)?;
|
||||
|
||||
// expect a valid expression
|
||||
let expr = self.parse_expression()?;
|
||||
|
||||
let _ = expect_tt!(self.next()?, Semicolon);
|
||||
|
||||
// return statement
|
||||
return ParseResult::Accept(Statement::Declaration {
|
||||
var: name,
|
||||
value: Some(expr),
|
||||
});
|
||||
}
|
||||
|
||||
// handle assignment without "let"
|
||||
let name = expect_value!(self.peek_next()?, Identifier);
|
||||
if name.accepted() {
|
||||
let varname = name?;
|
||||
if expect_tt!(self.peek(1)?, LeftParen).accepted() {
|
||||
let expr = self.parse_expression()?; // a function call expr
|
||||
let _ = expect_tt!(self.next()?, Semicolon)?;
|
||||
return ParseResult::Accept(Statement::Expression { expr });
|
||||
}
|
||||
|
||||
self.next()?;
|
||||
let _ = expect_tt!(self.next()?, Assign)?;
|
||||
|
||||
let value = self.parse_expression()?;
|
||||
|
||||
let _ = expect_tt!(self.next()?, Semicolon);
|
||||
|
||||
return ParseResult::Accept(Statement::Assign {
|
||||
varname: varname.name,
|
||||
value,
|
||||
});
|
||||
}
|
||||
|
||||
ParseResult::Reject(CompilerError::UnexpectedToken(
|
||||
self.peek_next()?.tt().to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
fn parse_expression(&mut self) -> ParseResult<Expression, CompilerError> {
|
||||
self.parse_comparison()
|
||||
}
|
||||
|
||||
fn parse_comparison(&mut self) -> ParseResult<Expression, CompilerError> {
|
||||
let mut expr = self.parse_additive()?;
|
||||
|
||||
while let Some(op) = match self.peek_next()? {
|
||||
Token::EqualEqual => Some(BinaryOperator::Ne),
|
||||
Token::BangEqual => Some(BinaryOperator::Ne),
|
||||
Token::Less => Some(BinaryOperator::Lt),
|
||||
Token::Greater => Some(BinaryOperator::Gt),
|
||||
Token::LessEqual => Some(BinaryOperator::Le),
|
||||
Token::GreaterEqual => Some(BinaryOperator::Ge),
|
||||
_ => None,
|
||||
} {
|
||||
self.next()?;
|
||||
let right = Box::new(self.parse_additive()?);
|
||||
expr = Expression::Binary {
|
||||
op,
|
||||
left: Box::new(expr),
|
||||
right,
|
||||
}
|
||||
}
|
||||
|
||||
ParseResult::Accept(expr)
|
||||
}
|
||||
|
||||
fn parse_additive(&mut self) -> ParseResult<Expression, CompilerError> {
|
||||
let left = self.parse_multiplicative()?;
|
||||
|
||||
let op = match self.peek_next()? {
|
||||
Token::Plus => BinaryOperator::Add,
|
||||
Token::Minus => BinaryOperator::Sub,
|
||||
_ => return ParseResult::Accept(left),
|
||||
};
|
||||
|
||||
self.next()?;
|
||||
ParseResult::Accept(Expression::Binary {
|
||||
op,
|
||||
left: Box::new(left),
|
||||
right: Box::new(self.parse_additive()?),
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_multiplicative(&mut self) -> ParseResult<Expression, CompilerError> {
|
||||
let left = self.parse_unary()?;
|
||||
|
||||
let op = match self.peek_next()? {
|
||||
Token::Star => BinaryOperator::Mul,
|
||||
Token::Slash => BinaryOperator::Div,
|
||||
_ => return ParseResult::Accept(left),
|
||||
};
|
||||
|
||||
self.next()?;
|
||||
ParseResult::Accept(Expression::Binary {
|
||||
op,
|
||||
left: Box::new(left),
|
||||
right: Box::new(self.parse_multiplicative()?),
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_unary(&mut self) -> ParseResult<Expression, CompilerError> {
|
||||
let op = match self.peek_next()? {
|
||||
Token::Plus => UnaryOperator::Plus,
|
||||
Token::Minus => UnaryOperator::Minus,
|
||||
Token::Star => UnaryOperator::Dereference,
|
||||
Token::Amphersand => UnaryOperator::Reference,
|
||||
_ => return ParseResult::Accept(self.parse_primary()?),
|
||||
};
|
||||
|
||||
self.next()?;
|
||||
let operand = Box::new(self.parse_unary()?);
|
||||
ParseResult::Accept(Expression::Unary { op, operand })
|
||||
}
|
||||
|
||||
fn parse_primary(&mut self) -> ParseResult<Expression, CompilerError> {
|
||||
match self.peek_next()? {
|
||||
Token::Integer(value) => {
|
||||
self.next()?;
|
||||
ParseResult::Accept(Expression::Number(value as isize))
|
||||
}
|
||||
Token::String(value) => {
|
||||
self.next()?;
|
||||
ParseResult::Accept(Expression::StringLiteral(value))
|
||||
}
|
||||
Token::Identifier(_) => {
|
||||
let name = expect_value!(self.next()?, Identifier)?;
|
||||
|
||||
if matches!(self.peek_next()?, Token::LeftParen) {
|
||||
// Function call
|
||||
self.next()?;
|
||||
let mut args = Vec::new();
|
||||
|
||||
if !matches!(self.peek_next()?, Token::RightParen) {
|
||||
args.push(self.parse_expression()?);
|
||||
|
||||
while matches!(self.peek_next()?, Token::Comma) {
|
||||
self.next()?;
|
||||
args.push(self.parse_expression()?);
|
||||
}
|
||||
}
|
||||
|
||||
let _ = expect_tt!(self.next()?, RightParen)?;
|
||||
|
||||
ParseResult::Accept(Expression::Call { name, args })
|
||||
} else {
|
||||
ParseResult::Accept(Expression::Variable {
|
||||
name,
|
||||
expr_type: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
Token::LeftParen => {
|
||||
self.next()?;
|
||||
let expr = self.parse_expression()?;
|
||||
let _ = expect_tt!(self.next()?, RightParen)?;
|
||||
ParseResult::Accept(expr)
|
||||
}
|
||||
_ => ParseResult::Reject(CompilerError::UnexpectedToken(
|
||||
self.peek_next()?.tt().to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_var_decl(&mut self) -> ParseResult<Variable, CompilerError> {
|
||||
let name = expect_value!(self.next()?, Identifier)?;
|
||||
|
||||
let _ = expect_tt!(self.next()?, Colon)?;
|
||||
|
||||
let type_id = self.parse_type()?;
|
||||
|
||||
ParseResult::Accept(Variable {
|
||||
name: name.name,
|
||||
type_id,
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_type(&mut self) -> ParseResult<TypeId, CompilerError> {
|
||||
// get the type name incl namespace
|
||||
let typename = expect_value!(self.next()?, Identifier)?;
|
||||
|
||||
match typename.name.as_str() {
|
||||
"u32" => ParseResult::Accept(TypeId::U32),
|
||||
"u16" => ParseResult::Accept(TypeId::U16),
|
||||
"u8" => ParseResult::Accept(TypeId::U8),
|
||||
"i32" => ParseResult::Accept(TypeId::I32),
|
||||
"i16" => ParseResult::Accept(TypeId::I16),
|
||||
"i8" => ParseResult::Accept(TypeId::I8),
|
||||
"void" => ParseResult::Accept(TypeId::Void),
|
||||
"char" => ParseResult::Accept(TypeId::Char),
|
||||
"str" => ParseResult::Accept(TypeId::Ptr(Box::new(TypeId::Char))),
|
||||
_ => todo!("Implement parsing for other types!!"),
|
||||
}
|
||||
}
|
||||
|
||||
fn next(&mut self) -> ParseResult<Token, CompilerError> {
|
||||
if self.idx >= self.tokens.len() {
|
||||
ParseResult::Reject(CompilerError::UnexpectedEndOfInput)
|
||||
} else {
|
||||
let token = self.tokens[self.idx].clone();
|
||||
self.idx += 1;
|
||||
ParseResult::Accept(token)
|
||||
}
|
||||
}
|
||||
|
||||
fn peek_next(&self) -> ParseResult<Token, CompilerError> {
|
||||
if self.idx >= self.tokens.len() {
|
||||
ParseResult::Reject(CompilerError::UnexpectedEndOfInput)
|
||||
} else {
|
||||
ParseResult::Accept(self.tokens[self.idx].clone())
|
||||
}
|
||||
}
|
||||
|
||||
fn peek(&self, offset: usize) -> ParseResult<Token, CompilerError> {
|
||||
if self.idx + offset >= self.tokens.len() {
|
||||
ParseResult::Reject(CompilerError::UnexpectedEndOfInput)
|
||||
} else {
|
||||
ParseResult::Accept(self.tokens[self.idx + offset].clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, E> ParseResult<T, E> {
|
||||
pub fn accepted(&self) -> bool {
|
||||
matches!(self, ParseResult::Accept(_))
|
||||
}
|
||||
}
|
||||
|
||||
pub enum ParseResultResidual<T> {
|
||||
Deny,
|
||||
Reject(T),
|
||||
}
|
||||
|
||||
impl<T, E> Try for ParseResult<T, E> {
|
||||
type Output = T;
|
||||
type Residual = ParseResultResidual<E>;
|
||||
|
||||
fn from_output(output: T) -> Self {
|
||||
ParseResult::Accept(output)
|
||||
}
|
||||
|
||||
fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
|
||||
match self {
|
||||
ParseResult::Accept(v) => ControlFlow::Continue(v),
|
||||
ParseResult::Deny => ControlFlow::Break(ParseResultResidual::Deny),
|
||||
ParseResult::Reject(e) => ControlFlow::Break(ParseResultResidual::Reject(e)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, E> FromResidual for ParseResult<T, E> {
|
||||
fn from_residual(residual: ParseResultResidual<E>) -> Self {
|
||||
match residual {
|
||||
ParseResultResidual::Deny => ParseResult::Deny,
|
||||
ParseResultResidual::Reject(e) => ParseResult::Reject(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! expect_tt {
|
||||
($token:expr, $($variant:ident),+) => {{
|
||||
let token = $token.clone();
|
||||
let tt = token.tt().to_string();
|
||||
|
||||
let mut vs = String::new();
|
||||
$(
|
||||
let s = stringify!($variant);
|
||||
vs.push_str(s);
|
||||
vs.push_str("|");
|
||||
)+
|
||||
|
||||
match tt.as_str() {
|
||||
$(
|
||||
stringify!($variant) => ParseResult::Accept(token),
|
||||
)+
|
||||
_ => {
|
||||
// let expected = format!("[{}]", vec![$(stringify!($variant)),+].join(" | "));
|
||||
ParseResult::Reject(CompilerError::UnexpectedToken(tt))
|
||||
}
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! expect_value {
|
||||
($expr:expr, $variant:ident) => {{
|
||||
let tok = $expr;
|
||||
match tok.clone() {
|
||||
Token::$variant(value) => ParseResult::Accept(value),
|
||||
_ => {
|
||||
ParseResult::Reject(CompilerError::UnexpectedToken(tok.tt().to_string()))
|
||||
}
|
||||
}
|
||||
}};
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
use crate::model::{CompilerError, Program};
|
||||
|
||||
pub struct Analyser;
|
||||
|
||||
impl Analyser {
|
||||
pub fn new() -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
pub fn analyse(&self, _ast: Program) -> Result<(), CompilerError> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
use crate::model::{CompilerError, Program};
|
||||
|
||||
mod c;
|
||||
mod dsc;
|
||||
|
||||
pub fn compiler_frontend(ext: &str, data: &str) -> Result<Program, CompilerError> {
|
||||
match ext {
|
||||
"dsc" => Ok(dsc::generate_ast(&data)?),
|
||||
"c" => Ok(c::generate_ast(&data)?),
|
||||
_ => Err(CompilerError::Generic(format!(
|
||||
"File type {} not supported",
|
||||
ext
|
||||
))),
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user