Compare commits
16 Commits
52f9b67018
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| b0acadc601 | |||
| 6b13991e39 | |||
| 7bcf77f1c1 | |||
| 2d87760c79 | |||
| 09a99bbc33 | |||
| 1de7e2876c | |||
| e3aa2e21c0 | |||
| 5099bbaab7 | |||
| 6ff9c804aa | |||
| daae12d695 | |||
| ef3dcb41aa | |||
| 4dabf32c2a | |||
| 58a2e69611 | |||
| b6b45bd27c | |||
| b7c2924e8b | |||
| 55636e07af |
@@ -3,3 +3,5 @@
|
|||||||
/output
|
/output
|
||||||
*.pyc
|
*.pyc
|
||||||
*.zip
|
*.zip
|
||||||
|
*.ir
|
||||||
|
*.s
|
||||||
Vendored
+31
@@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
// Use IntelliSense to learn about possible attributes.
|
||||||
|
// Hover to view descriptions of existing attributes.
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "Debug executable 'compiler'",
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"cargo": {
|
||||||
|
"args": [
|
||||||
|
"run",
|
||||||
|
"--bin=compiler"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"args": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Debug unit tests in executable 'compiler'",
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"cargo": {
|
||||||
|
"args": [
|
||||||
|
"test",
|
||||||
|
"--bin=compiler"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
use crate::diagnostic::span::Span;
|
||||||
|
|
||||||
|
pub struct Type {
|
||||||
|
span: Span,
|
||||||
|
kind: TypeKind,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum TypeKind {
|
||||||
|
Builtin(BuiltinType),
|
||||||
|
Array(ArrayType),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct BuiltinType {
|
||||||
|
span: Span,
|
||||||
|
kind: BuiltinTypeKind,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum BuiltinTypeKind {
|
||||||
|
Int,
|
||||||
|
Void,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ArrayType {
|
||||||
|
span: Span,
|
||||||
|
element_type: Box<Type>,
|
||||||
|
dimensions: Vec<ArrayDimension>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ArrayDimension {
|
||||||
|
span: Span,
|
||||||
|
size: Option<Expr>,
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
use crate::lexer::types::TokenValue;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Error)]
|
||||||
|
pub enum ParseError {
|
||||||
|
#[error("expected {} before {}", .1, .0)]
|
||||||
|
ExpectedBefore(TokenValue, &'static str),
|
||||||
|
#[error("expected identifier after {}", .0)]
|
||||||
|
ExpectedIdentAfter(TokenValue),
|
||||||
|
}
|
||||||
+52
-1
@@ -2,7 +2,7 @@ use petgraph::dot::{Config, Dot};
|
|||||||
use petgraph::graph::{Graph, NodeIndex};
|
use petgraph::graph::{Graph, NodeIndex};
|
||||||
|
|
||||||
use crate::ast::types::{
|
use crate::ast::types::{
|
||||||
ArrayDimension, BlockStmt, BreakStmt, CompileUnit, ContinueStmt, Expr, ExprValue, FuncDeclStmt, GlobalDeclStmt, IfStmt, Param, ReturnStmt, Statement, VarDeclStmt, VarDeclStmtValue, WhileStmt
|
ArrayDimension, BlockStmt, BreakStmt, CompileUnit, ContinueStmt, Expr, ExprValue, ForInit, ForStmt, FuncDeclStmt, GlobalDeclStmt, IfStmt, Param, ReturnStmt, Statement, VarDeclStmt, VarDeclStmtValue, WhileStmt
|
||||||
};
|
};
|
||||||
|
|
||||||
pub type AstGraph = Graph<String, String>;
|
pub type AstGraph = Graph<String, String>;
|
||||||
@@ -77,6 +77,9 @@ impl AstGraphBuilder {
|
|||||||
for dimension in &value.dimensions {
|
for dimension in &value.dimensions {
|
||||||
self.add_array_dimension(node, dimension);
|
self.add_array_dimension(node, dimension);
|
||||||
}
|
}
|
||||||
|
if let Some(expr) = &value.value {
|
||||||
|
self.add_expr(node, expr);
|
||||||
|
}
|
||||||
node
|
node
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,6 +137,7 @@ impl AstGraphBuilder {
|
|||||||
},
|
},
|
||||||
Statement::If(if_stmt) => self.add_if_stmt(parent, if_stmt),
|
Statement::If(if_stmt) => self.add_if_stmt(parent, if_stmt),
|
||||||
Statement::While(while_stmt) => self.add_while_stmt(parent, while_stmt),
|
Statement::While(while_stmt) => self.add_while_stmt(parent, while_stmt),
|
||||||
|
Statement::For(for_stmt) => self.add_for_stmt(parent, for_stmt),
|
||||||
Statement::Break(break_stmt) => self.add_break_stmt(parent, break_stmt),
|
Statement::Break(break_stmt) => self.add_break_stmt(parent, break_stmt),
|
||||||
Statement::Continue(continue_stmt) => self.add_continue_stmt(parent, continue_stmt),
|
Statement::Continue(continue_stmt) => self.add_continue_stmt(parent, continue_stmt),
|
||||||
}
|
}
|
||||||
@@ -160,6 +164,42 @@ impl AstGraphBuilder {
|
|||||||
self.add_block_stmt(node, &while_stmt.body);
|
self.add_block_stmt(node, &while_stmt.body);
|
||||||
node
|
node
|
||||||
}
|
}
|
||||||
|
fn add_for_stmt(&mut self, parent: NodeIndex, for_stmt: &ForStmt) -> NodeIndex {
|
||||||
|
let node = self.child(parent, for_stmt.to_string());
|
||||||
|
match &for_stmt.init {
|
||||||
|
Some(ForInit::Expr(expr)) => {
|
||||||
|
let init = self.child(node, "Init");
|
||||||
|
self.add_expr(init, expr);
|
||||||
|
}
|
||||||
|
Some(ForInit::VarDecl(var_decl)) => {
|
||||||
|
let init = self.child(node, "Init");
|
||||||
|
self.add_var_decl(init, var_decl);
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
self.child(node, "NoInit");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match &for_stmt.condition {
|
||||||
|
Some(expr) => {
|
||||||
|
let condition = self.child(node, "Condition");
|
||||||
|
self.add_expr(condition, expr);
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
self.child(node, "NoCondition");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match &for_stmt.update {
|
||||||
|
Some(expr) => {
|
||||||
|
let update = self.child(node, "Update");
|
||||||
|
self.add_expr(update, expr);
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
self.child(node, "NoUpdate");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.add_block_stmt(node, &for_stmt.body);
|
||||||
|
node
|
||||||
|
}
|
||||||
fn add_break_stmt(&mut self, parent: NodeIndex, break_stmt: &BreakStmt) -> NodeIndex {
|
fn add_break_stmt(&mut self, parent: NodeIndex, break_stmt: &BreakStmt) -> NodeIndex {
|
||||||
self.child(parent, break_stmt.to_string())
|
self.child(parent, break_stmt.to_string())
|
||||||
}
|
}
|
||||||
@@ -195,11 +235,22 @@ impl AstGraphBuilder {
|
|||||||
self.add_expr(node, rvalue);
|
self.add_expr(node, rvalue);
|
||||||
node
|
node
|
||||||
},
|
},
|
||||||
|
ExprValue::CompoundAssign { lvalue, rvalue, .. } => {
|
||||||
|
let node = self.child(parent, expr.value.to_string());
|
||||||
|
self.add_expr(node, lvalue);
|
||||||
|
self.add_expr(node, rvalue);
|
||||||
|
node
|
||||||
|
},
|
||||||
ExprValue::UnaryOp { op: _, operand } => {
|
ExprValue::UnaryOp { op: _, operand } => {
|
||||||
let node = self.child(parent, expr.value.to_string());
|
let node = self.child(parent, expr.value.to_string());
|
||||||
self.add_expr(node, operand);
|
self.add_expr(node, operand);
|
||||||
node
|
node
|
||||||
}
|
}
|
||||||
|
ExprValue::IncDec { operand, .. } => {
|
||||||
|
let node = self.child(parent, expr.value.to_string());
|
||||||
|
self.add_expr(node, operand);
|
||||||
|
node
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,2 +1,4 @@
|
|||||||
|
pub mod err;
|
||||||
pub mod graph;
|
pub mod graph;
|
||||||
|
pub mod parser;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
|||||||
@@ -0,0 +1,459 @@
|
|||||||
|
use crate::{
|
||||||
|
ast::err::ParseError,
|
||||||
|
ast::types::{
|
||||||
|
ArrayDimension, CompileUnit, Expr, FuncDeclStmt, GlobalDeclStmt, Param, StorageClass,
|
||||||
|
VarDeclStmt, VarDeclStmtValue,
|
||||||
|
},
|
||||||
|
diagnostic::span::Span,
|
||||||
|
lexer::types::{TokenValue, TypeIdent},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{ParseProcessError, ParseType, Parser};
|
||||||
|
|
||||||
|
struct DeclSpecifiers {
|
||||||
|
storage_class: Option<StorageClass>,
|
||||||
|
type_specifier: TypeIdent,
|
||||||
|
type_span: Span,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Declarator {
|
||||||
|
name: String,
|
||||||
|
name_span: Span,
|
||||||
|
suffixes: Vec<DeclaratorSuffix>,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum DeclaratorSuffix {
|
||||||
|
Array(ArrayDimension),
|
||||||
|
Function {
|
||||||
|
params: Vec<Param>,
|
||||||
|
span: Span,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
struct InitDeclarator {
|
||||||
|
declarator: Declarator,
|
||||||
|
initializer: Option<Expr>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parser {
|
||||||
|
pub(super) fn parse_compile_unit(&mut self) -> CompileUnit {
|
||||||
|
let mut global_decls = vec![];
|
||||||
|
while !self.is_eof() {
|
||||||
|
if let Some(decl) = self.parse_external_decl_stmt() {
|
||||||
|
global_decls.push(decl);
|
||||||
|
} else {
|
||||||
|
self.until_next_token(&[TokenValue::Semicolon, TokenValue::RBrace]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CompileUnit { global_decls }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_external_decl_stmt(&mut self) -> Option<GlobalDeclStmt> {
|
||||||
|
let specifiers = match self.parse_decl_specifiers(ParseType::MustParse) {
|
||||||
|
Ok(specifiers) => specifiers,
|
||||||
|
Err(ParseProcessError::ErrorInMatch) => return None,
|
||||||
|
Err(ParseProcessError::TryNext) => unreachable!(),
|
||||||
|
};
|
||||||
|
let declarator = match self.parse_declarator() {
|
||||||
|
Ok(declarator) => declarator,
|
||||||
|
Err(ParseProcessError::ErrorInMatch) => return None,
|
||||||
|
Err(ParseProcessError::TryNext) => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
if self.peek().value == TokenValue::LBrace {
|
||||||
|
match self.finish_func_def_stmt(specifiers, declarator) {
|
||||||
|
Ok(func_decl) => return Some(GlobalDeclStmt::FuncDecl(func_decl)),
|
||||||
|
Err(ParseProcessError::ErrorInMatch) => return None,
|
||||||
|
Err(ParseProcessError::TryNext) => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match self.finish_var_decl_stmt(specifiers, declarator, true, true) {
|
||||||
|
Ok(var_decl) => Some(GlobalDeclStmt::VarDecl(var_decl)),
|
||||||
|
Err(ParseProcessError::ErrorInMatch) => None,
|
||||||
|
Err(ParseProcessError::TryNext) => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_decl_specifiers(
|
||||||
|
&mut self,
|
||||||
|
parse_type: ParseType,
|
||||||
|
) -> Result<DeclSpecifiers, ParseProcessError> {
|
||||||
|
let storage_class = if self.peek().value == TokenValue::Static {
|
||||||
|
self.advance(1);
|
||||||
|
Some(StorageClass::Static)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
let type_token = self.peek().clone();
|
||||||
|
let type_specifier = match type_token.value.as_type_ident() {
|
||||||
|
Some(type_specifier) => {
|
||||||
|
self.advance(1);
|
||||||
|
type_specifier
|
||||||
|
}
|
||||||
|
None if parse_type == ParseType::TryParse && storage_class.is_none() => {
|
||||||
|
return Err(ParseProcessError::TryNext);
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
let token = self.next();
|
||||||
|
self.diagnostics.add_from_frontend_error(
|
||||||
|
ParseError::ExpectedBefore(token.value, "type specifier"),
|
||||||
|
token.span,
|
||||||
|
);
|
||||||
|
return Err(ParseProcessError::ErrorInMatch);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(DeclSpecifiers {
|
||||||
|
storage_class,
|
||||||
|
type_specifier,
|
||||||
|
type_span: type_token.span,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_declarator(&mut self) -> Result<Declarator, ParseProcessError> {
|
||||||
|
self.parse_direct_declarator()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_direct_declarator(&mut self) -> Result<Declarator, ParseProcessError> {
|
||||||
|
let mut declarator = match self.peek().value.clone() {
|
||||||
|
TokenValue::Ident(name) => {
|
||||||
|
let name_span = self.peek().span;
|
||||||
|
self.advance(1);
|
||||||
|
Declarator {
|
||||||
|
name,
|
||||||
|
name_span,
|
||||||
|
suffixes: vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TokenValue::LParen => {
|
||||||
|
self.advance(1);
|
||||||
|
let declarator = self.parse_declarator()?;
|
||||||
|
self.must_match_token(&TokenValue::RParen, "`)`")?;
|
||||||
|
declarator
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let token = self.next();
|
||||||
|
self.diagnostics.add_from_frontend_error(
|
||||||
|
ParseError::ExpectedBefore(token.value, "declarator"),
|
||||||
|
token.span,
|
||||||
|
);
|
||||||
|
return Err(ParseProcessError::ErrorInMatch);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match self.peek().value {
|
||||||
|
TokenValue::LBracket => {
|
||||||
|
let dimension = self.parse_array_declarator_suffix()?;
|
||||||
|
declarator.suffixes.push(DeclaratorSuffix::Array(dimension));
|
||||||
|
}
|
||||||
|
TokenValue::LParen => {
|
||||||
|
let start_span = self.next().span;
|
||||||
|
let params = self.parse_parameter_list_after_lparen()?;
|
||||||
|
let end_span = self.last().span;
|
||||||
|
declarator.suffixes.push(DeclaratorSuffix::Function {
|
||||||
|
params,
|
||||||
|
span: Span::from_two(start_span, end_span),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_ => break,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(declarator)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_array_declarator_suffix(&mut self) -> Result<ArrayDimension, ParseProcessError> {
|
||||||
|
let start_span = self.next().span;
|
||||||
|
let value = if self.peek().value == TokenValue::RBracket {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(self.parse_expr()?)
|
||||||
|
};
|
||||||
|
let end_span = match &self.peek().value {
|
||||||
|
TokenValue::RBracket => {
|
||||||
|
let span = self.peek().span;
|
||||||
|
self.advance(1);
|
||||||
|
span
|
||||||
|
}
|
||||||
|
// TokenValue::Eof => {
|
||||||
|
// self.diagnostics.add_from_frontend_error(
|
||||||
|
// ParseError::ExpectedBefore(TokenValue::Eof, "`]`"),
|
||||||
|
// start_span,
|
||||||
|
// );
|
||||||
|
// return Err(ParseProcessError::ErrorInMatch);
|
||||||
|
// }
|
||||||
|
_ => {
|
||||||
|
let token = self.next();
|
||||||
|
self.diagnostics.add_from_frontend_error(
|
||||||
|
ParseError::ExpectedBefore(token.value, "`]`"),
|
||||||
|
token.span,
|
||||||
|
);
|
||||||
|
return Err(ParseProcessError::ErrorInMatch);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(ArrayDimension {
|
||||||
|
value,
|
||||||
|
span: Span::from_two(start_span, end_span),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_parameter_list_after_lparen(&mut self) -> Result<Vec<Param>, ParseProcessError> {
|
||||||
|
if self.peek().value == TokenValue::RParen {
|
||||||
|
self.advance(1);
|
||||||
|
return Ok(vec![]);
|
||||||
|
}
|
||||||
|
if self.peek().value == TokenValue::TypeIdent(TypeIdent::Void)
|
||||||
|
&& self.peek_n(1).value == TokenValue::RParen
|
||||||
|
{
|
||||||
|
self.advance(2);
|
||||||
|
return Ok(vec![]);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut params = vec![];
|
||||||
|
loop {
|
||||||
|
match self.parse_param() {
|
||||||
|
Ok(param) => params.push(param),
|
||||||
|
Err(error) => {
|
||||||
|
self.until_next_token(&[TokenValue::Comma, TokenValue::RParen]);
|
||||||
|
if self.last().value == TokenValue::RParen {
|
||||||
|
return Err(error);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match self.peek().value {
|
||||||
|
TokenValue::Comma => {
|
||||||
|
self.advance(1);
|
||||||
|
}
|
||||||
|
TokenValue::RParen => {
|
||||||
|
self.advance(1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
self.must_match_token(&TokenValue::RParen, "`,` or `)`")?;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(params)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_param(&mut self) -> Result<Param, ParseProcessError> {
|
||||||
|
let specifiers = self.parse_decl_specifiers(ParseType::MustParse)?;
|
||||||
|
let declarator = self.parse_declarator()?;
|
||||||
|
self.declarator_to_param(specifiers, declarator)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn parse_var_decl_stmt(
|
||||||
|
&mut self,
|
||||||
|
parse_type: ParseType,
|
||||||
|
) -> Result<VarDeclStmt, ParseProcessError> {
|
||||||
|
self.parse_var_decl_stmt_inner(parse_type, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn parse_var_decl_stmt_inner(
|
||||||
|
&mut self,
|
||||||
|
parse_type: ParseType,
|
||||||
|
consume_semicolon: bool,
|
||||||
|
) -> Result<VarDeclStmt, ParseProcessError> {
|
||||||
|
let specifiers = self.parse_decl_specifiers(parse_type)?;
|
||||||
|
let declarator = self.parse_declarator()?;
|
||||||
|
self.finish_var_decl_stmt(specifiers, declarator, consume_semicolon, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn finish_func_def_stmt(
|
||||||
|
&mut self,
|
||||||
|
specifiers: DeclSpecifiers,
|
||||||
|
declarator: Declarator,
|
||||||
|
) -> Result<FuncDeclStmt, ParseProcessError> {
|
||||||
|
let Declarator {
|
||||||
|
name,
|
||||||
|
name_span,
|
||||||
|
suffixes,
|
||||||
|
} = declarator;
|
||||||
|
let params = self.take_function_params(name_span, suffixes)?;
|
||||||
|
let body = self.parse_block_stmt(ParseType::MustParse)?;
|
||||||
|
Ok(FuncDeclStmt {
|
||||||
|
return_type: specifiers.type_specifier.into(),
|
||||||
|
storage_class: specifiers.storage_class,
|
||||||
|
name,
|
||||||
|
params,
|
||||||
|
body,
|
||||||
|
ret_type_span: specifiers.type_span,
|
||||||
|
name_span,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn finish_var_decl_stmt(
|
||||||
|
&mut self,
|
||||||
|
specifiers: DeclSpecifiers,
|
||||||
|
first_declarator: Declarator,
|
||||||
|
consume_semicolon: bool,
|
||||||
|
_allow_empty_declarator_list: bool,
|
||||||
|
) -> Result<VarDeclStmt, ParseProcessError> {
|
||||||
|
let mut values = vec![];
|
||||||
|
|
||||||
|
let initializer = self.parse_initializer()?;
|
||||||
|
values.push(self.init_declarator_to_var_value(InitDeclarator {
|
||||||
|
declarator: first_declarator,
|
||||||
|
initializer,
|
||||||
|
})?);
|
||||||
|
|
||||||
|
while self.peek().value == TokenValue::Comma {
|
||||||
|
self.advance(1);
|
||||||
|
let declarator = self.parse_declarator()?;
|
||||||
|
let initializer = self.parse_initializer()?;
|
||||||
|
values.push(self.init_declarator_to_var_value(InitDeclarator {
|
||||||
|
declarator,
|
||||||
|
initializer,
|
||||||
|
})?);
|
||||||
|
}
|
||||||
|
|
||||||
|
if consume_semicolon {
|
||||||
|
self.must_match_token(&TokenValue::Semicolon, "`;`")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(VarDeclStmt {
|
||||||
|
storage_class: specifiers.storage_class,
|
||||||
|
values,
|
||||||
|
type_span: specifiers.type_span,
|
||||||
|
data_type: specifiers.type_specifier.into(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_initializer(&mut self) -> Result<Option<Expr>, ParseProcessError> {
|
||||||
|
if self.peek().value != TokenValue::Equal {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
self.advance(1);
|
||||||
|
Ok(Some(self.parse_expr()?))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn skip_initializer_list(&mut self) -> Result<(), ParseProcessError> {
|
||||||
|
let mut depth = 0usize;
|
||||||
|
loop {
|
||||||
|
let token = self.next().clone();
|
||||||
|
match token.value {
|
||||||
|
TokenValue::LBrace => depth += 1,
|
||||||
|
TokenValue::RBrace => {
|
||||||
|
depth -= 1;
|
||||||
|
if depth == 0 {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TokenValue::Eof => {
|
||||||
|
self.diagnostics.add_from_frontend_error(
|
||||||
|
ParseError::ExpectedBefore(TokenValue::Eof, "`}`"),
|
||||||
|
token.span,
|
||||||
|
);
|
||||||
|
return Err(ParseProcessError::ErrorInMatch);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_declarator_to_var_value(
|
||||||
|
&mut self,
|
||||||
|
init_declarator: InitDeclarator,
|
||||||
|
) -> Result<VarDeclStmtValue, ParseProcessError> {
|
||||||
|
let Declarator {
|
||||||
|
name,
|
||||||
|
name_span,
|
||||||
|
suffixes,
|
||||||
|
} = init_declarator.declarator;
|
||||||
|
let dimensions = self.take_array_dimensions(suffixes)?;
|
||||||
|
Ok(VarDeclStmtValue {
|
||||||
|
name,
|
||||||
|
name_span,
|
||||||
|
dimensions,
|
||||||
|
value: init_declarator.initializer,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn declarator_to_param(
|
||||||
|
&mut self,
|
||||||
|
specifiers: DeclSpecifiers,
|
||||||
|
declarator: Declarator,
|
||||||
|
) -> Result<Param, ParseProcessError> {
|
||||||
|
let Declarator {
|
||||||
|
name,
|
||||||
|
name_span,
|
||||||
|
suffixes,
|
||||||
|
} = declarator;
|
||||||
|
let dimensions = self.take_array_dimensions(suffixes)?;
|
||||||
|
Ok(Param {
|
||||||
|
param_type: specifiers.type_specifier.into(),
|
||||||
|
name,
|
||||||
|
dimensions,
|
||||||
|
name_span,
|
||||||
|
type_span: specifiers.type_span,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn take_array_dimensions(
|
||||||
|
&mut self,
|
||||||
|
suffixes: Vec<DeclaratorSuffix>,
|
||||||
|
) -> Result<Vec<ArrayDimension>, ParseProcessError> {
|
||||||
|
let mut dimensions = vec![];
|
||||||
|
for suffix in suffixes {
|
||||||
|
match suffix {
|
||||||
|
DeclaratorSuffix::Array(dimension) => dimensions.push(dimension),
|
||||||
|
DeclaratorSuffix::Function { span, .. } => {
|
||||||
|
self.diagnostics.add_from_frontend_error(
|
||||||
|
ParseError::ExpectedBefore(TokenValue::LParen, "variable declarator"),
|
||||||
|
span,
|
||||||
|
);
|
||||||
|
return Err(ParseProcessError::ErrorInMatch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(dimensions)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn take_function_params(
|
||||||
|
&mut self,
|
||||||
|
name_span: Span,
|
||||||
|
suffixes: Vec<DeclaratorSuffix>,
|
||||||
|
) -> Result<Vec<Param>, ParseProcessError> {
|
||||||
|
let mut params = None;
|
||||||
|
for suffix in suffixes {
|
||||||
|
match suffix {
|
||||||
|
DeclaratorSuffix::Function {
|
||||||
|
params: suffix_params,
|
||||||
|
..
|
||||||
|
} if params.is_none() => params = Some(suffix_params),
|
||||||
|
DeclaratorSuffix::Function { span, .. } => {
|
||||||
|
self.diagnostics.add_from_frontend_error(
|
||||||
|
ParseError::ExpectedBefore(TokenValue::LParen, "function body"),
|
||||||
|
span,
|
||||||
|
);
|
||||||
|
return Err(ParseProcessError::ErrorInMatch);
|
||||||
|
}
|
||||||
|
DeclaratorSuffix::Array(dimension) => {
|
||||||
|
self.diagnostics.add_from_frontend_error(
|
||||||
|
ParseError::ExpectedBefore(TokenValue::LBracket, "function declarator"),
|
||||||
|
dimension.span,
|
||||||
|
);
|
||||||
|
return Err(ParseProcessError::ErrorInMatch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match params {
|
||||||
|
Some(params) => Ok(params),
|
||||||
|
None => {
|
||||||
|
self.diagnostics.add_from_frontend_error(
|
||||||
|
ParseError::ExpectedBefore(TokenValue::LBrace, "function declarator"),
|
||||||
|
name_span,
|
||||||
|
);
|
||||||
|
Err(ParseProcessError::ErrorInMatch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,380 @@
|
|||||||
|
use crate::{
|
||||||
|
ast::{err::ParseError, types::{BinaryOp, Expr, ExprValue, IncDecOp, UnaryOp}},
|
||||||
|
diagnostic::span::Span,
|
||||||
|
lexer::types::{Token, TokenValue},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{ParseProcessError, Parser};
|
||||||
|
|
||||||
|
impl Parser {
|
||||||
|
fn parse_primary(&mut self) -> Result<Expr, ParseProcessError> {
|
||||||
|
let token = self.next().clone();
|
||||||
|
match token.value {
|
||||||
|
TokenValue::Ident(name) => {
|
||||||
|
if matches!(self.peek().value, TokenValue::LParen) {
|
||||||
|
self.advance(1);
|
||||||
|
let mut args = vec![];
|
||||||
|
loop {
|
||||||
|
match &self.peek().value {
|
||||||
|
TokenValue::RParen => {
|
||||||
|
let end_span = self.peek().span;
|
||||||
|
self.advance(1);
|
||||||
|
return Ok(Expr {
|
||||||
|
value: ExprValue::FuncCall(name, args),
|
||||||
|
span: Span::from_two(token.span, end_span),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
TokenValue::Eof => {
|
||||||
|
self.diagnostics.add_from_frontend_error(
|
||||||
|
ParseError::ExpectedBefore(TokenValue::Eof, "`)`"),
|
||||||
|
token.span,
|
||||||
|
);
|
||||||
|
return Err(ParseProcessError::ErrorInMatch);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
args.push(self.parse_expr()?);
|
||||||
|
match &self.peek().value {
|
||||||
|
TokenValue::Comma => {
|
||||||
|
self.advance(1);
|
||||||
|
}
|
||||||
|
TokenValue::RParen => {
|
||||||
|
let end_span = self.peek().span;
|
||||||
|
self.advance(1);
|
||||||
|
return Ok(Expr {
|
||||||
|
value: ExprValue::FuncCall(name, args),
|
||||||
|
span: Span::from_two(token.span, end_span),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let token = self.next();
|
||||||
|
self.diagnostics.add_from_frontend_error(
|
||||||
|
ParseError::ExpectedBefore(token.value, "`,` or `)`"),
|
||||||
|
token.span,
|
||||||
|
);
|
||||||
|
return Err(ParseProcessError::ErrorInMatch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(Expr {
|
||||||
|
value: ExprValue::Var(name),
|
||||||
|
span: token.span,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
TokenValue::IntLit(value) => Ok(Expr {
|
||||||
|
value: ExprValue::IntLit(value),
|
||||||
|
span: token.span,
|
||||||
|
}),
|
||||||
|
TokenValue::LParen => {
|
||||||
|
let expr = self.parse_expr()?;
|
||||||
|
match &self.peek().value {
|
||||||
|
TokenValue::RParen => {
|
||||||
|
let end_span = self.peek().span;
|
||||||
|
self.advance(1);
|
||||||
|
Ok(Expr {
|
||||||
|
span: Span::from_two(token.span, end_span),
|
||||||
|
..expr
|
||||||
|
})
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let token = self.next();
|
||||||
|
self.diagnostics.add_from_frontend_error(
|
||||||
|
ParseError::ExpectedBefore(token.value, "`)`"),
|
||||||
|
token.span,
|
||||||
|
);
|
||||||
|
Err(ParseProcessError::ErrorInMatch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
self.diagnostics.add_from_frontend_error(
|
||||||
|
ParseError::ExpectedBefore(token.value, "expression"),
|
||||||
|
token.span,
|
||||||
|
);
|
||||||
|
Err(ParseProcessError::ErrorInMatch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn parse_postfix(&mut self) -> Result<Expr, ParseProcessError> {
|
||||||
|
let mut expr = self.parse_primary()?;
|
||||||
|
loop {
|
||||||
|
if self.peek().value == TokenValue::LBracket {
|
||||||
|
let start_span = expr.span;
|
||||||
|
self.advance(1);
|
||||||
|
let index = self.parse_expr()?;
|
||||||
|
let end_span = match &self.peek().value {
|
||||||
|
TokenValue::RBracket => {
|
||||||
|
let span = self.peek().span;
|
||||||
|
self.advance(1);
|
||||||
|
span
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let token = self.next();
|
||||||
|
self.diagnostics.add_from_frontend_error(
|
||||||
|
ParseError::ExpectedBefore(token.value, "`]`"),
|
||||||
|
token.span,
|
||||||
|
);
|
||||||
|
return Err(ParseProcessError::ErrorInMatch);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
expr = Expr {
|
||||||
|
value: ExprValue::ArrayAccess {
|
||||||
|
array: Box::new(expr),
|
||||||
|
index: Box::new(index),
|
||||||
|
},
|
||||||
|
span: Span::from_two(start_span, end_span),
|
||||||
|
};
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if let TokenValue::PlusPlus | TokenValue::MinusMinus = self.peek().value {
|
||||||
|
let op = if self.peek().value == TokenValue::PlusPlus {
|
||||||
|
IncDecOp::Inc
|
||||||
|
} else {
|
||||||
|
IncDecOp::Dec
|
||||||
|
};
|
||||||
|
let end_span = self.peek().span;
|
||||||
|
self.advance(1);
|
||||||
|
expr = Expr {
|
||||||
|
span: Span::from_two(expr.span, end_span),
|
||||||
|
value: ExprValue::IncDec {
|
||||||
|
op,
|
||||||
|
operand: Box::new(expr),
|
||||||
|
is_prefix: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Ok(expr)
|
||||||
|
}
|
||||||
|
fn parse_unary(&mut self) -> Result<Expr, ParseProcessError> {
|
||||||
|
let token = self.peek().clone();
|
||||||
|
match token.value {
|
||||||
|
TokenValue::Plus => {
|
||||||
|
self.advance(1);
|
||||||
|
let expr = self.parse_unary()?;
|
||||||
|
let span = Span::from_two(token.span, expr.span);
|
||||||
|
Ok(Expr {
|
||||||
|
value: ExprValue::UnaryOp {
|
||||||
|
op: UnaryOp::Add,
|
||||||
|
operand: Box::new(expr),
|
||||||
|
},
|
||||||
|
span,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
TokenValue::Minus => {
|
||||||
|
self.advance(1);
|
||||||
|
let rhs = self.parse_unary()?;
|
||||||
|
let span = Span::from_two(token.span, rhs.span);
|
||||||
|
Ok(Expr {
|
||||||
|
value: ExprValue::UnaryOp {
|
||||||
|
op: UnaryOp::Sub,
|
||||||
|
operand: Box::new(rhs),
|
||||||
|
},
|
||||||
|
span,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
TokenValue::Not => {
|
||||||
|
self.advance(1);
|
||||||
|
let rhs = self.parse_unary()?;
|
||||||
|
let span = Span::from_two(token.span, rhs.span);
|
||||||
|
Ok(Expr {
|
||||||
|
value: ExprValue::UnaryOp {
|
||||||
|
op: UnaryOp::Not,
|
||||||
|
operand: Box::new(rhs),
|
||||||
|
},
|
||||||
|
span,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
TokenValue::PlusPlus | TokenValue::MinusMinus => {
|
||||||
|
let op = if token.value == TokenValue::PlusPlus {
|
||||||
|
IncDecOp::Inc
|
||||||
|
} else {
|
||||||
|
IncDecOp::Dec
|
||||||
|
};
|
||||||
|
self.advance(1);
|
||||||
|
let operand = self.parse_unary()?;
|
||||||
|
let span = Span::from_two(token.span, operand.span);
|
||||||
|
Ok(Expr {
|
||||||
|
value: ExprValue::IncDec {
|
||||||
|
op,
|
||||||
|
operand: Box::new(operand),
|
||||||
|
is_prefix: true,
|
||||||
|
},
|
||||||
|
span,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
_ => self.parse_postfix(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn parse_multiplicative(&mut self) -> Result<Expr, ParseProcessError> {
|
||||||
|
let mut left = self.parse_unary()?;
|
||||||
|
loop {
|
||||||
|
let op = match self.peek().value {
|
||||||
|
TokenValue::Star => BinaryOp::Mul,
|
||||||
|
TokenValue::Slash => BinaryOp::Div,
|
||||||
|
TokenValue::Percent => BinaryOp::Mod,
|
||||||
|
_ => break,
|
||||||
|
};
|
||||||
|
self.advance(1);
|
||||||
|
let right = self.parse_unary()?;
|
||||||
|
let span = Span::from_two(left.span, right.span);
|
||||||
|
left = Expr {
|
||||||
|
value: ExprValue::BinaryOp {
|
||||||
|
lhs: Box::new(left),
|
||||||
|
op,
|
||||||
|
rhs: Box::new(right),
|
||||||
|
},
|
||||||
|
span,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Ok(left)
|
||||||
|
}
|
||||||
|
fn parse_additive(&mut self) -> Result<Expr, ParseProcessError> {
|
||||||
|
let mut left = self.parse_multiplicative()?;
|
||||||
|
loop {
|
||||||
|
let op = match self.peek().value {
|
||||||
|
TokenValue::Plus => BinaryOp::Add,
|
||||||
|
TokenValue::Minus => BinaryOp::Sub,
|
||||||
|
_ => break,
|
||||||
|
};
|
||||||
|
self.advance(1);
|
||||||
|
let right = self.parse_multiplicative()?;
|
||||||
|
let span = Span::from_two(left.span, right.span);
|
||||||
|
left = Expr {
|
||||||
|
value: ExprValue::BinaryOp {
|
||||||
|
lhs: Box::new(left),
|
||||||
|
op,
|
||||||
|
rhs: Box::new(right),
|
||||||
|
},
|
||||||
|
span,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Ok(left)
|
||||||
|
}
|
||||||
|
fn parse_relational(&mut self) -> Result<Expr, ParseProcessError> {
|
||||||
|
let mut left = self.parse_additive()?;
|
||||||
|
loop {
|
||||||
|
let op = match self.peek().value {
|
||||||
|
TokenValue::Less => BinaryOp::Less,
|
||||||
|
TokenValue::Greater => BinaryOp::Greater,
|
||||||
|
TokenValue::LessEqual => BinaryOp::LessEqual,
|
||||||
|
TokenValue::GreaterEqual => BinaryOp::GreaterEqual,
|
||||||
|
_ => break,
|
||||||
|
};
|
||||||
|
self.advance(1);
|
||||||
|
let right = self.parse_additive()?;
|
||||||
|
let span = Span::from_two(left.span, right.span);
|
||||||
|
left = Expr {
|
||||||
|
value: ExprValue::BinaryOp {
|
||||||
|
lhs: Box::new(left),
|
||||||
|
op,
|
||||||
|
rhs: Box::new(right),
|
||||||
|
},
|
||||||
|
span,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Ok(left)
|
||||||
|
}
|
||||||
|
fn parse_equality(&mut self) -> Result<Expr, ParseProcessError> {
|
||||||
|
let mut left = self.parse_relational()?;
|
||||||
|
loop {
|
||||||
|
let op = match self.peek().value {
|
||||||
|
TokenValue::DoubleEqual => BinaryOp::Equal,
|
||||||
|
TokenValue::NotEqual => BinaryOp::NotEqual,
|
||||||
|
_ => break,
|
||||||
|
};
|
||||||
|
self.advance(1);
|
||||||
|
let right = self.parse_relational()?;
|
||||||
|
let span = Span::from_two(left.span, right.span);
|
||||||
|
left = Expr {
|
||||||
|
value: ExprValue::BinaryOp {
|
||||||
|
lhs: Box::new(left),
|
||||||
|
op,
|
||||||
|
rhs: Box::new(right),
|
||||||
|
},
|
||||||
|
span,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Ok(left)
|
||||||
|
}
|
||||||
|
fn parse_logical_and(&mut self) -> Result<Expr, ParseProcessError> {
|
||||||
|
let mut left = self.parse_equality()?;
|
||||||
|
loop {
|
||||||
|
let op = match self.peek().value {
|
||||||
|
TokenValue::And => BinaryOp::And,
|
||||||
|
_ => break,
|
||||||
|
};
|
||||||
|
self.advance(1);
|
||||||
|
let right = self.parse_equality()?;
|
||||||
|
let span = Span::from_two(left.span, right.span);
|
||||||
|
left = Expr {
|
||||||
|
value: ExprValue::BinaryOp {
|
||||||
|
lhs: Box::new(left),
|
||||||
|
op,
|
||||||
|
rhs: Box::new(right),
|
||||||
|
},
|
||||||
|
span,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Ok(left)
|
||||||
|
}
|
||||||
|
fn parse_logical_or(&mut self) -> Result<Expr, ParseProcessError> {
|
||||||
|
let mut left = self.parse_logical_and()?;
|
||||||
|
loop {
|
||||||
|
let op = match self.peek().value {
|
||||||
|
TokenValue::Or => BinaryOp::Or,
|
||||||
|
_ => break,
|
||||||
|
};
|
||||||
|
self.advance(1);
|
||||||
|
let right = self.parse_logical_and()?;
|
||||||
|
let span = Span::from_two(left.span, right.span);
|
||||||
|
left = Expr {
|
||||||
|
value: ExprValue::BinaryOp {
|
||||||
|
lhs: Box::new(left),
|
||||||
|
op,
|
||||||
|
rhs: Box::new(right),
|
||||||
|
},
|
||||||
|
span,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Ok(left)
|
||||||
|
}
|
||||||
|
fn parse_assign(&mut self) -> Result<Expr, ParseProcessError> {
|
||||||
|
let lvalue = self.parse_logical_or()?;
|
||||||
|
let compound_op = match self.peek().value {
|
||||||
|
TokenValue::Equal => None,
|
||||||
|
TokenValue::PlusEqual => Some(BinaryOp::Add),
|
||||||
|
TokenValue::MinusEqual => Some(BinaryOp::Sub),
|
||||||
|
TokenValue::StarEqual => Some(BinaryOp::Mul),
|
||||||
|
TokenValue::SlashEqual => Some(BinaryOp::Div),
|
||||||
|
TokenValue::PercentEqual => Some(BinaryOp::Mod),
|
||||||
|
_ => return Ok(lvalue),
|
||||||
|
};
|
||||||
|
self.advance(1);
|
||||||
|
let rvalue = self.parse_assign()?;
|
||||||
|
let span = Span::from_two(lvalue.span, rvalue.span);
|
||||||
|
if let Some(op) = compound_op {
|
||||||
|
return Ok(Expr {
|
||||||
|
value: ExprValue::CompoundAssign {
|
||||||
|
lvalue: Box::new(lvalue),
|
||||||
|
op,
|
||||||
|
rvalue: Box::new(rvalue),
|
||||||
|
},
|
||||||
|
span,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Ok(Expr {
|
||||||
|
value: ExprValue::Assign {
|
||||||
|
lvalue: Box::new(lvalue),
|
||||||
|
rvalue: Box::new(rvalue),
|
||||||
|
},
|
||||||
|
span,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
pub(super) fn parse_expr(&mut self) -> Result<Expr, ParseProcessError> {
|
||||||
|
self.parse_assign()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
use crate::{
|
||||||
|
ast::err::ParseError,
|
||||||
|
ast::types::CompileUnit,
|
||||||
|
diagnostic::{Diagnositics, span::Span},
|
||||||
|
lexer::types::{Token, TokenValue},
|
||||||
|
};
|
||||||
|
|
||||||
|
mod decl;
|
||||||
|
mod expr;
|
||||||
|
mod stmt;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests;
|
||||||
|
|
||||||
|
pub struct Parser {
|
||||||
|
tokens: Vec<Token>,
|
||||||
|
eof_token: Token,
|
||||||
|
pub diagnostics: Diagnositics,
|
||||||
|
pos: usize,
|
||||||
|
}
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
enum ParseType {
|
||||||
|
MustParse,
|
||||||
|
TryParse,
|
||||||
|
}
|
||||||
|
enum ParseProcessError {
|
||||||
|
TryNext,
|
||||||
|
ErrorInMatch
|
||||||
|
}
|
||||||
|
impl Parser {
|
||||||
|
pub fn new(tokens: Vec<Token>, diagnostics: Diagnositics) -> Self {
|
||||||
|
let eof_pos = tokens.last().map_or(0, |token| token.span.end);
|
||||||
|
Self {
|
||||||
|
tokens,
|
||||||
|
eof_token: Token {
|
||||||
|
value: TokenValue::Eof,
|
||||||
|
span: Span {
|
||||||
|
start: eof_pos,
|
||||||
|
end: eof_pos,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
diagnostics,
|
||||||
|
pos: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn parse(&mut self) -> CompileUnit {
|
||||||
|
self.parse_compile_unit()
|
||||||
|
}
|
||||||
|
fn peek(&self) -> &Token {
|
||||||
|
self.tokens.get(self.pos).unwrap_or(&self.eof_token)
|
||||||
|
}
|
||||||
|
fn peek_n(&self, n: usize) -> &Token {
|
||||||
|
self.tokens.get(self.pos + n).unwrap_or(&self.eof_token)
|
||||||
|
}
|
||||||
|
fn next(&mut self) -> Token {
|
||||||
|
let token = self.peek().clone();
|
||||||
|
if !matches!(token.value, TokenValue::Eof) {
|
||||||
|
self.pos += 1;
|
||||||
|
}
|
||||||
|
token
|
||||||
|
}
|
||||||
|
fn advance(&mut self, n: usize) {
|
||||||
|
self.pos += n;
|
||||||
|
assert!(self.pos <= self.tokens.len());
|
||||||
|
}
|
||||||
|
fn back(&mut self, n: usize) {
|
||||||
|
assert!(self.pos >= n);
|
||||||
|
self.pos -= n;
|
||||||
|
}
|
||||||
|
fn last(&self) -> &Token {
|
||||||
|
if self.pos == 0 {
|
||||||
|
&self.eof_token
|
||||||
|
} else {
|
||||||
|
self.tokens.get(self.pos - 1).unwrap_or(&self.eof_token)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn is_eof(&self) -> bool {
|
||||||
|
matches!(self.peek().value, TokenValue::Eof)
|
||||||
|
}
|
||||||
|
fn must_match_token(&mut self, expected: &TokenValue, diagnostic_text: &'static str) -> Result<(), ParseProcessError> {
|
||||||
|
if &self.peek().value == expected {
|
||||||
|
self.advance(1);
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
let token = self.next();
|
||||||
|
self.diagnostics.add_from_frontend_error(
|
||||||
|
ParseError::ExpectedBefore(token.value, diagnostic_text),
|
||||||
|
token.span,
|
||||||
|
);
|
||||||
|
Err(ParseProcessError::ErrorInMatch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn must_have_some(&mut self, diagnostic_text: &'static str) -> Result<(), ParseProcessError> {
|
||||||
|
if self.is_eof() {
|
||||||
|
let span = self.last().span;
|
||||||
|
self.diagnostics
|
||||||
|
.add_from_frontend_error(ParseError::ExpectedBefore(TokenValue::Eof, diagnostic_text), span);
|
||||||
|
return Err(ParseProcessError::ErrorInMatch);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
|
||||||
|
}
|
||||||
|
fn until_next_token(&mut self, expected: &[TokenValue]) {
|
||||||
|
while !self.is_eof() {
|
||||||
|
if expected.contains(&self.peek().value) {
|
||||||
|
self.advance(1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
self.advance(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,290 @@
|
|||||||
|
use crate::{
|
||||||
|
ast::err::ParseError,
|
||||||
|
ast::types::{
|
||||||
|
BlockStmt, BreakStmt, ContinueStmt, ForInit, ForStmt, IfElseBranch, IfStmt, ReturnStmt,
|
||||||
|
Statement, WhileStmt,
|
||||||
|
},
|
||||||
|
lexer::types::TokenValue,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{ParseProcessError, ParseType, Parser};
|
||||||
|
|
||||||
|
impl Parser {
|
||||||
|
pub(super) fn parse_block_stmt(&mut self, parse_type: ParseType) -> Result<BlockStmt, ParseProcessError> {
|
||||||
|
if self.peek().value != TokenValue::LBrace {
|
||||||
|
if parse_type == ParseType::MustParse {
|
||||||
|
let token = self.next().clone();
|
||||||
|
self.diagnostics.add_from_frontend_error(
|
||||||
|
ParseError::ExpectedBefore(token.value, "`{`"),
|
||||||
|
token.span,
|
||||||
|
);
|
||||||
|
return Err(ParseProcessError::ErrorInMatch);
|
||||||
|
}
|
||||||
|
return Err(ParseProcessError::TryNext);
|
||||||
|
}
|
||||||
|
self.advance(1);
|
||||||
|
let mut statements = vec![];
|
||||||
|
// println!("parse block stmt");
|
||||||
|
loop {
|
||||||
|
if self.is_eof() {
|
||||||
|
let span = self.last().span;
|
||||||
|
self.diagnostics
|
||||||
|
.add_from_frontend_error(ParseError::ExpectedBefore(TokenValue::Eof, "`}`"), span);
|
||||||
|
return Err(ParseProcessError::ErrorInMatch);
|
||||||
|
}
|
||||||
|
if matches!(self.peek().value, TokenValue::Semicolon) {
|
||||||
|
// like a();;
|
||||||
|
self.advance(1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if matches!(self.peek().value, TokenValue::RBrace) {
|
||||||
|
self.advance(1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// parse statement here
|
||||||
|
match self.parse_stmt() {
|
||||||
|
Ok(stmt) => statements.push(stmt),
|
||||||
|
Err(_) => {
|
||||||
|
self.until_next_token(&[TokenValue::Semicolon, TokenValue::RBrace]);
|
||||||
|
if self.last().value == TokenValue::RBrace {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// println!("finish parse block stmt");
|
||||||
|
Ok(BlockStmt {
|
||||||
|
statements
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_stmt(&mut self) -> Result<Statement, ParseProcessError> {
|
||||||
|
if self.peek().value == TokenValue::Semicolon {
|
||||||
|
self.advance(1);
|
||||||
|
return Ok(Statement::Block(BlockStmt { statements: vec![] }));
|
||||||
|
}
|
||||||
|
match self.parse_var_decl_stmt(ParseType::TryParse) {
|
||||||
|
Ok(var_decl) => return Ok(Statement::VarDecl(var_decl)),
|
||||||
|
Err(ParseProcessError::ErrorInMatch) => return Err(ParseProcessError::ErrorInMatch),
|
||||||
|
Err(_) => {},
|
||||||
|
};
|
||||||
|
match self.parse_return_stmt() {
|
||||||
|
Ok(return_stmt) => return Ok(Statement::Return(return_stmt)),
|
||||||
|
Err(ParseProcessError::ErrorInMatch) => return Err(ParseProcessError::ErrorInMatch),
|
||||||
|
Err(_) => {},
|
||||||
|
};
|
||||||
|
match self.parse_if_stmt() {
|
||||||
|
Ok(if_stmt) => return Ok(Statement::If(if_stmt)),
|
||||||
|
Err(ParseProcessError::ErrorInMatch) => return Err(ParseProcessError::ErrorInMatch),
|
||||||
|
Err(_) => {},
|
||||||
|
}
|
||||||
|
match self.parse_while_stmt() {
|
||||||
|
Ok(while_stmt) => return Ok(Statement::While(while_stmt)),
|
||||||
|
Err(ParseProcessError::ErrorInMatch) => return Err(ParseProcessError::ErrorInMatch),
|
||||||
|
Err(_) => {},
|
||||||
|
}
|
||||||
|
match self.parse_for_stmt() {
|
||||||
|
Ok(for_stmt) => return Ok(Statement::For(for_stmt)),
|
||||||
|
Err(ParseProcessError::ErrorInMatch) => return Err(ParseProcessError::ErrorInMatch),
|
||||||
|
Err(_) => {},
|
||||||
|
}
|
||||||
|
match self.parse_break_stmt() {
|
||||||
|
Ok(break_stmt) => return Ok(Statement::Break(break_stmt)),
|
||||||
|
Err(ParseProcessError::ErrorInMatch) => return Err(ParseProcessError::ErrorInMatch),
|
||||||
|
Err(_) => {},
|
||||||
|
}
|
||||||
|
match self.parse_continue_stmt() {
|
||||||
|
Ok(continue_stmt) => return Ok(Statement::Continue(continue_stmt)),
|
||||||
|
Err(ParseProcessError::ErrorInMatch) => return Err(ParseProcessError::ErrorInMatch),
|
||||||
|
Err(_) => {},
|
||||||
|
}
|
||||||
|
match self.parse_block_stmt(ParseType::TryParse) {
|
||||||
|
Ok(block_stmt) => return Ok(Statement::Block(block_stmt)),
|
||||||
|
Err(ParseProcessError::ErrorInMatch) => return Err(ParseProcessError::ErrorInMatch),
|
||||||
|
Err(_) => {},
|
||||||
|
}
|
||||||
|
match self.parse_expr() {
|
||||||
|
Ok(expr) => {
|
||||||
|
self.must_match_token(&TokenValue::Semicolon, "`;`")?;
|
||||||
|
return Ok(Statement::Expr(expr))
|
||||||
|
},
|
||||||
|
Err(ParseProcessError::ErrorInMatch) => return Err(ParseProcessError::ErrorInMatch),
|
||||||
|
Err(_) => {},
|
||||||
|
}
|
||||||
|
self.until_next_token(&[TokenValue::Semicolon]);
|
||||||
|
Err(ParseProcessError::ErrorInMatch)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_return_stmt(&mut self) -> Result<ReturnStmt, ParseProcessError> {
|
||||||
|
if self.peek().value != TokenValue::Return {
|
||||||
|
return Err(ParseProcessError::TryNext);
|
||||||
|
}
|
||||||
|
let span = self.next().span;
|
||||||
|
let value = if matches!(self.peek().value, TokenValue::Semicolon) {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(self.parse_expr()?)
|
||||||
|
};
|
||||||
|
self.must_match_token(&TokenValue::Semicolon, "`;`")?;
|
||||||
|
Ok(ReturnStmt {
|
||||||
|
value,
|
||||||
|
span,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
fn parse_if_stmt(&mut self) -> Result<IfStmt, ParseProcessError> {
|
||||||
|
if self.peek().value != TokenValue::If {
|
||||||
|
return Err(ParseProcessError::TryNext);
|
||||||
|
}
|
||||||
|
self.advance(1);
|
||||||
|
self.must_match_token(&TokenValue::LParen, "`(`")?;
|
||||||
|
self.must_have_some("if condition expr")?;
|
||||||
|
let condition = self.parse_expr()?;
|
||||||
|
self.must_match_token(&TokenValue::RParen, "`)`")?;
|
||||||
|
let then_branch;
|
||||||
|
let mut ifelse_branch = vec![];
|
||||||
|
let mut else_branch = None;
|
||||||
|
self.must_have_some("if statement body")?;
|
||||||
|
if self.peek().value != TokenValue::LBrace {
|
||||||
|
let stmt = self.parse_stmt()?;
|
||||||
|
then_branch = BlockStmt { statements: vec![stmt] };
|
||||||
|
} else {
|
||||||
|
then_branch = self.parse_block_stmt(ParseType::MustParse)?;
|
||||||
|
}
|
||||||
|
loop {
|
||||||
|
if self.is_eof() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if self.peek().value == TokenValue::Else {
|
||||||
|
self.advance(1);
|
||||||
|
self.must_have_some("else body")?;
|
||||||
|
if self.peek().value != TokenValue::If {
|
||||||
|
self.back(1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// else if
|
||||||
|
self.advance(1);
|
||||||
|
self.must_match_token(&TokenValue::LParen, "`(`")?;
|
||||||
|
self.must_have_some("else if condition expr")?;
|
||||||
|
let condition = self.parse_expr()?;
|
||||||
|
self.must_match_token(&TokenValue::RParen, "`)`")?;
|
||||||
|
let then_branch;
|
||||||
|
if self.peek().value != TokenValue::LBrace {
|
||||||
|
let stmt = self.parse_stmt()?;
|
||||||
|
then_branch = BlockStmt { statements: vec![stmt] };
|
||||||
|
} else {
|
||||||
|
then_branch = self.parse_block_stmt(ParseType::MustParse)?;
|
||||||
|
}
|
||||||
|
ifelse_branch.push(IfElseBranch { condition, then_branch });
|
||||||
|
} else {
|
||||||
|
// if end ?
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if self.peek().value == TokenValue::Else {
|
||||||
|
// Parse else branch
|
||||||
|
self.advance(1);
|
||||||
|
self.must_have_some("else body")?;
|
||||||
|
if self.peek().value != TokenValue::LBrace {
|
||||||
|
let stmt = self.parse_stmt()?;
|
||||||
|
else_branch = Some(BlockStmt { statements: vec![stmt] });
|
||||||
|
} else {
|
||||||
|
else_branch = Some(self.parse_block_stmt(ParseType::MustParse)?);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(IfStmt {
|
||||||
|
condition,
|
||||||
|
then_branch,
|
||||||
|
ifelse_branch,
|
||||||
|
else_branch,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_while_stmt(&mut self) -> Result<WhileStmt, ParseProcessError> {
|
||||||
|
if self.peek().value != TokenValue::While {
|
||||||
|
return Err(ParseProcessError::TryNext);
|
||||||
|
}
|
||||||
|
self.advance(1);
|
||||||
|
self.must_match_token(&TokenValue::LParen, "`(`")?;
|
||||||
|
self.must_have_some("while condition expr")?;
|
||||||
|
let condition = self.parse_expr()?;
|
||||||
|
self.must_match_token(&TokenValue::RParen, "`)`")?;
|
||||||
|
let body;
|
||||||
|
if self.peek().value != TokenValue::LBrace {
|
||||||
|
let stmt = self.parse_stmt()?;
|
||||||
|
body = BlockStmt { statements: vec![stmt] };
|
||||||
|
} else {
|
||||||
|
body = self.parse_block_stmt(ParseType::MustParse)?;
|
||||||
|
}
|
||||||
|
Ok(WhileStmt {
|
||||||
|
condition,
|
||||||
|
body,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_for_stmt(&mut self) -> Result<ForStmt, ParseProcessError> {
|
||||||
|
if self.peek().value != TokenValue::For {
|
||||||
|
return Err(ParseProcessError::TryNext);
|
||||||
|
}
|
||||||
|
self.advance(1);
|
||||||
|
self.must_match_token(&TokenValue::LParen, "`(`")?;
|
||||||
|
let init = if self.peek().value == TokenValue::Semicolon {
|
||||||
|
None
|
||||||
|
} else if matches!(self.peek().value, TokenValue::Static | TokenValue::TypeIdent(_)) {
|
||||||
|
Some(ForInit::VarDecl(self.parse_var_decl_stmt_inner(ParseType::MustParse, false)?))
|
||||||
|
} else {
|
||||||
|
Some(ForInit::Expr(self.parse_expr()?))
|
||||||
|
};
|
||||||
|
self.must_match_token(&TokenValue::Semicolon, "`;`")?;
|
||||||
|
let condition = if self.peek().value == TokenValue::Semicolon {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(self.parse_expr()?)
|
||||||
|
};
|
||||||
|
self.must_match_token(&TokenValue::Semicolon, "`;`")?;
|
||||||
|
let update = if self.peek().value == TokenValue::RParen {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(self.parse_expr()?)
|
||||||
|
};
|
||||||
|
self.must_match_token(&TokenValue::RParen, "`)`")?;
|
||||||
|
let body;
|
||||||
|
if self.peek().value != TokenValue::LBrace {
|
||||||
|
let stmt = self.parse_stmt()?;
|
||||||
|
body = BlockStmt { statements: vec![stmt] };
|
||||||
|
} else {
|
||||||
|
body = self.parse_block_stmt(ParseType::MustParse)?;
|
||||||
|
}
|
||||||
|
Ok(ForStmt {
|
||||||
|
init,
|
||||||
|
condition,
|
||||||
|
update,
|
||||||
|
body,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_break_stmt(&mut self) -> Result<BreakStmt, ParseProcessError> {
|
||||||
|
let start_span = self.peek().span;
|
||||||
|
if self.peek().value == TokenValue::Break {
|
||||||
|
self.advance(1);
|
||||||
|
self.must_match_token(&TokenValue::Semicolon, "`;`")?;
|
||||||
|
Ok(BreakStmt {
|
||||||
|
span: start_span,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(ParseProcessError::TryNext)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_continue_stmt(&mut self) -> Result<ContinueStmt, ParseProcessError> {
|
||||||
|
let start_span = self.peek().span;
|
||||||
|
if self.peek().value == TokenValue::Continue {
|
||||||
|
self.advance(1);
|
||||||
|
self.must_match_token(&TokenValue::Semicolon, "`;`")?;
|
||||||
|
Ok(ContinueStmt {
|
||||||
|
span: start_span,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(ParseProcessError::TryNext)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
use std::io::BufRead;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::fs::File;
|
||||||
|
use crate::ast::graph::AstGraphExt;
|
||||||
|
use crate::lexer::lexer::Lexer;
|
||||||
|
use crate::utils::case_list::CaseList;
|
||||||
|
use crate::utils::num_sequence::NumberSequence;
|
||||||
|
|
||||||
|
pub use super::*;
|
||||||
|
fn test_case(case_str: &str) {
|
||||||
|
let case_sequence = NumberSequence::from_str(case_str).unwrap();
|
||||||
|
let case_list = CaseList::from_dir(&Path::new("./testcases")).unwrap();
|
||||||
|
let mut error_case_cnt = 0;
|
||||||
|
for case_no in case_sequence {
|
||||||
|
let case_path = case_list.get_case_path(case_no).unwrap();
|
||||||
|
println!("{}", case_path.display());
|
||||||
|
let file = File::open(&case_path).unwrap();
|
||||||
|
let mut buf_reader = std::io::BufReader::new(file);
|
||||||
|
let mut lexer = Lexer::new();
|
||||||
|
let mut full_text = String::new();
|
||||||
|
loop {
|
||||||
|
let mut line = String::new();
|
||||||
|
let bytes_read = buf_reader.read_line(&mut line).unwrap();
|
||||||
|
if bytes_read == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
full_text.push_str(&line);
|
||||||
|
lexer.parse_next_str(&line);
|
||||||
|
}
|
||||||
|
let (tokens, diagnostics) = lexer.finish();
|
||||||
|
let mut is_error = false;
|
||||||
|
if !diagnostics.is_empty() {
|
||||||
|
diagnostics.print(&format!("{}", case_path.display()), &full_text);
|
||||||
|
is_error = true;
|
||||||
|
}
|
||||||
|
let mut parser = Parser::new(tokens, diagnostics);
|
||||||
|
let compile_unit = parser.parse_compile_unit();
|
||||||
|
let dot = compile_unit.to_dot();
|
||||||
|
let case_name = case_list.get_case_name(case_no).unwrap().strip_suffix(".c").unwrap();
|
||||||
|
std::fs::write(format!("output/{}.dot", case_name), dot).unwrap();
|
||||||
|
if !parser.diagnostics.is_empty() {
|
||||||
|
parser.diagnostics.print(&format!("{}", case_path.display()), &full_text);
|
||||||
|
is_error = true;
|
||||||
|
}
|
||||||
|
if is_error {
|
||||||
|
error_case_cnt += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if error_case_cnt > 0 {
|
||||||
|
panic!("Found {} cases with errors", error_case_cnt);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_expr() {
|
||||||
|
test_case("0-3,14-25");
|
||||||
|
// test_case("0-3,14-25");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_if_while() {
|
||||||
|
test_case("26-32,34-41,46-51,57");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_error() {
|
||||||
|
test_case("999");
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_func() {
|
||||||
|
test_case("12-13,58-60");
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_array() {
|
||||||
|
test_case("4-7,11,42,71,74-78,82,84,86,90,95,96,100,101,103");
|
||||||
|
}
|
||||||
+53
-2
@@ -1,4 +1,4 @@
|
|||||||
use crate::{diagnostic::span::Span, frontend::types::{Token, TokenValue, TypeIdent}};
|
use crate::{diagnostic::span::Span, lexer::types::{TokenValue, TypeIdent}};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
pub struct CompileUnit {
|
pub struct CompileUnit {
|
||||||
@@ -8,9 +8,13 @@ pub enum GlobalDeclStmt {
|
|||||||
VarDecl(VarDeclStmt),
|
VarDecl(VarDeclStmt),
|
||||||
FuncDecl(FuncDeclStmt),
|
FuncDecl(FuncDeclStmt),
|
||||||
}
|
}
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum StorageClass {
|
||||||
|
Static,
|
||||||
|
}
|
||||||
pub struct VarDeclStmt {
|
pub struct VarDeclStmt {
|
||||||
pub values: Vec<VarDeclStmtValue>,
|
pub values: Vec<VarDeclStmtValue>,
|
||||||
|
pub storage_class: Option<StorageClass>,
|
||||||
pub data_type: Type,
|
pub data_type: Type,
|
||||||
pub type_span: Span,
|
pub type_span: Span,
|
||||||
}
|
}
|
||||||
@@ -18,6 +22,7 @@ pub struct VarDeclStmtValue {
|
|||||||
pub name: String,
|
pub name: String,
|
||||||
pub name_span: Span,
|
pub name_span: Span,
|
||||||
pub dimensions: Vec<ArrayDimension>,
|
pub dimensions: Vec<ArrayDimension>,
|
||||||
|
pub value: Option<Expr>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ArrayDimension {
|
pub struct ArrayDimension {
|
||||||
@@ -27,6 +32,7 @@ pub struct ArrayDimension {
|
|||||||
|
|
||||||
pub struct FuncDeclStmt {
|
pub struct FuncDeclStmt {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
pub storage_class: Option<StorageClass>,
|
||||||
pub return_type: Type,
|
pub return_type: Type,
|
||||||
pub params: Vec<Param>,
|
pub params: Vec<Param>,
|
||||||
pub body: BlockStmt,
|
pub body: BlockStmt,
|
||||||
@@ -44,6 +50,7 @@ pub enum Statement {
|
|||||||
VarDecl(VarDeclStmt),
|
VarDecl(VarDeclStmt),
|
||||||
If(IfStmt),
|
If(IfStmt),
|
||||||
While(WhileStmt),
|
While(WhileStmt),
|
||||||
|
For(ForStmt),
|
||||||
Break(BreakStmt),
|
Break(BreakStmt),
|
||||||
Continue(ContinueStmt),
|
Continue(ContinueStmt),
|
||||||
}
|
}
|
||||||
@@ -77,6 +84,16 @@ pub struct WhileStmt {
|
|||||||
pub body: BlockStmt,
|
pub body: BlockStmt,
|
||||||
// pub span: Span,
|
// pub span: Span,
|
||||||
}
|
}
|
||||||
|
pub struct ForStmt {
|
||||||
|
pub init: Option<ForInit>,
|
||||||
|
pub condition: Option<Expr>,
|
||||||
|
pub update: Option<Expr>,
|
||||||
|
pub body: BlockStmt,
|
||||||
|
}
|
||||||
|
pub enum ForInit {
|
||||||
|
Expr(Expr),
|
||||||
|
VarDecl(VarDeclStmt),
|
||||||
|
}
|
||||||
pub struct BreakStmt {
|
pub struct BreakStmt {
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
}
|
}
|
||||||
@@ -87,10 +104,12 @@ pub struct ReturnStmt {
|
|||||||
pub value: Option<Expr>,
|
pub value: Option<Expr>,
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
}
|
}
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Expr {
|
pub struct Expr {
|
||||||
pub value: ExprValue,
|
pub value: ExprValue,
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
}
|
}
|
||||||
|
#[derive(Clone)]
|
||||||
pub enum ExprValue {
|
pub enum ExprValue {
|
||||||
IntLit(i64),
|
IntLit(i64),
|
||||||
Var(String),
|
Var(String),
|
||||||
@@ -107,11 +126,21 @@ pub enum ExprValue {
|
|||||||
op: UnaryOp,
|
op: UnaryOp,
|
||||||
operand: Box<Expr>,
|
operand: Box<Expr>,
|
||||||
},
|
},
|
||||||
|
IncDec {
|
||||||
|
op: IncDecOp,
|
||||||
|
operand: Box<Expr>,
|
||||||
|
is_prefix: bool,
|
||||||
|
},
|
||||||
FuncCall(String, Vec<Expr>),
|
FuncCall(String, Vec<Expr>),
|
||||||
Assign {
|
Assign {
|
||||||
lvalue: Box<Expr>,
|
lvalue: Box<Expr>,
|
||||||
rvalue: Box<Expr>
|
rvalue: Box<Expr>
|
||||||
},
|
},
|
||||||
|
CompoundAssign {
|
||||||
|
lvalue: Box<Expr>,
|
||||||
|
op: BinaryOp,
|
||||||
|
rvalue: Box<Expr>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub enum BinaryOp {
|
pub enum BinaryOp {
|
||||||
@@ -124,6 +153,11 @@ pub enum BinaryOp {
|
|||||||
pub enum UnaryOp {
|
pub enum UnaryOp {
|
||||||
Add, Sub, Not,
|
Add, Sub, Not,
|
||||||
}
|
}
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum IncDecOp {
|
||||||
|
Inc,
|
||||||
|
Dec,
|
||||||
|
}
|
||||||
impl BinaryOp {
|
impl BinaryOp {
|
||||||
pub fn from_token_value(token_value: &TokenValue) -> Option<Self> {
|
pub fn from_token_value(token_value: &TokenValue) -> Option<Self> {
|
||||||
match token_value {
|
match token_value {
|
||||||
@@ -227,6 +261,7 @@ impl fmt::Display for Statement {
|
|||||||
Statement::VarDecl(_) => write!(f, "VarDeclStmt"),
|
Statement::VarDecl(_) => write!(f, "VarDeclStmt"),
|
||||||
Statement::If(_) => write!(f, "IfStmt"),
|
Statement::If(_) => write!(f, "IfStmt"),
|
||||||
Statement::While(_) => write!(f, "WhileStmt"),
|
Statement::While(_) => write!(f, "WhileStmt"),
|
||||||
|
Statement::For(_) => write!(f, "ForStmt"),
|
||||||
Statement::Break(_) => write!(f, "BreakStmt"),
|
Statement::Break(_) => write!(f, "BreakStmt"),
|
||||||
Statement::Continue(_) => write!(f, "ContinueStmt"),
|
Statement::Continue(_) => write!(f, "ContinueStmt"),
|
||||||
}
|
}
|
||||||
@@ -249,6 +284,12 @@ impl fmt::Display for WhileStmt {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for ForStmt {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "ForStmt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl fmt::Display for BreakStmt {
|
impl fmt::Display for BreakStmt {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "BreakStmt")
|
write!(f, "BreakStmt")
|
||||||
@@ -276,7 +317,9 @@ impl fmt::Display for ExprValue {
|
|||||||
ExprValue::BinaryOp { op, .. } => write!(f, "BinaryOp({})", op),
|
ExprValue::BinaryOp { op, .. } => write!(f, "BinaryOp({})", op),
|
||||||
ExprValue::FuncCall(name, _) => write!(f, "FuncCall({})", name),
|
ExprValue::FuncCall(name, _) => write!(f, "FuncCall({})", name),
|
||||||
ExprValue::Assign { .. } => write!(f, "Assign"),
|
ExprValue::Assign { .. } => write!(f, "Assign"),
|
||||||
|
ExprValue::CompoundAssign { op, .. } => write!(f, "CompoundAssign({})", op),
|
||||||
ExprValue::UnaryOp { op, .. } => write!(f, "UnaryOp({})", op),
|
ExprValue::UnaryOp { op, .. } => write!(f, "UnaryOp({})", op),
|
||||||
|
ExprValue::IncDec { op, is_prefix, .. } => write!(f, "{}{}", if *is_prefix { "Prefix" } else { "Postfix" }, op),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -317,6 +360,14 @@ impl fmt::Display for UnaryOp {
|
|||||||
write!(f, "{}", op)
|
write!(f, "{}", op)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl fmt::Display for IncDecOp {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
IncDecOp::Inc => write!(f, "Inc"),
|
||||||
|
IncDecOp::Dec => write!(f, "Dec"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
impl fmt::Display for Type {
|
impl fmt::Display for Type {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
|
|||||||
+52
-14
@@ -3,8 +3,9 @@ use std::{fmt::Display, ops::Add};
|
|||||||
use crate::{backend::register_allocator::{REG_FP, REG_LR, REG_PC, REG_R0, REG_R1, REG_R2, REG_R3, REG_R12, REG_SP, Register}, ir::types::CmpOp as IRCmpOp};
|
use crate::{backend::register_allocator::{REG_FP, REG_LR, REG_PC, REG_R0, REG_R1, REG_R2, REG_R3, REG_R12, REG_SP, Register}, ir::types::CmpOp as IRCmpOp};
|
||||||
pub enum ARMInstr{
|
pub enum ARMInstr{
|
||||||
Move(MoveInstr),
|
Move(MoveInstr),
|
||||||
|
Movw(MovwInstr),
|
||||||
|
Movt(MovtInstr),
|
||||||
Load(LoadInstr),
|
Load(LoadInstr),
|
||||||
LoadPseudo(LoadPseudoInstr),
|
|
||||||
Store(StoreInstr),
|
Store(StoreInstr),
|
||||||
Mul(MulInstr),
|
Mul(MulInstr),
|
||||||
SDiv(SDivInstr),
|
SDiv(SDivInstr),
|
||||||
@@ -18,14 +19,16 @@ pub enum ARMInstr{
|
|||||||
Bl(BlInstr),
|
Bl(BlInstr),
|
||||||
B(BInstr),
|
B(BInstr),
|
||||||
Label(String),
|
Label(String),
|
||||||
|
LocalLabel(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for ARMInstr {
|
impl Display for ARMInstr {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
ARMInstr::Move(instr) => write!(f, "{}", instr),
|
ARMInstr::Move(instr) => write!(f, "{}", instr),
|
||||||
|
ARMInstr::Movw(instr) => write!(f, "{}", instr),
|
||||||
|
ARMInstr::Movt(instr) => write!(f, "{}", instr),
|
||||||
ARMInstr::Load(instr) => write!(f, "{}", instr),
|
ARMInstr::Load(instr) => write!(f, "{}", instr),
|
||||||
ARMInstr::LoadPseudo(instr) => write!(f, "{}", instr),
|
|
||||||
ARMInstr::Store(instr) => write!(f, "{}", instr),
|
ARMInstr::Store(instr) => write!(f, "{}", instr),
|
||||||
ARMInstr::Mul(instr) => write!(f, "{}", instr),
|
ARMInstr::Mul(instr) => write!(f, "{}", instr),
|
||||||
ARMInstr::SDiv(instr) => write!(f, "{}", instr),
|
ARMInstr::SDiv(instr) => write!(f, "{}", instr),
|
||||||
@@ -39,6 +42,7 @@ impl Display for ARMInstr {
|
|||||||
ARMInstr::B(instr) => write!(f, "{}", instr),
|
ARMInstr::B(instr) => write!(f, "{}", instr),
|
||||||
ARMInstr::FunctionHead(name, align_size) => write!(f, ".align {}\n.global {}\n.type {}, %function\n{}:", align_size, name, name, name),
|
ARMInstr::FunctionHead(name, align_size) => write!(f, ".align {}\n.global {}\n.type {}, %function\n{}:", align_size, name, name, name),
|
||||||
ARMInstr::Label(name) => write!(f, "{}:", name),
|
ARMInstr::Label(name) => write!(f, "{}:", name),
|
||||||
|
ARMInstr::LocalLabel(name) => write!(f, "{}:", name),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -127,6 +131,52 @@ impl Display for MoveInstr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum MoveWideOperand {
|
||||||
|
Imm(u16),
|
||||||
|
Symbol(String, String),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct MovwInstr(Register, MoveWideOperand);
|
||||||
|
impl MovwInstr {
|
||||||
|
pub fn new_imm(dest: Register, value: u16) -> ARMInstr {
|
||||||
|
ARMInstr::Movw(MovwInstr(dest, MoveWideOperand::Imm(value)))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_symbol(dest: Register, name: String, anchor: String) -> ARMInstr {
|
||||||
|
ARMInstr::Movw(MovwInstr(dest, MoveWideOperand::Symbol(name, anchor)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Display for MovwInstr {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let MovwInstr(dest, operand) = self;
|
||||||
|
match operand {
|
||||||
|
MoveWideOperand::Imm(value) => write!(f, "movw {}, #{}", dest, value),
|
||||||
|
MoveWideOperand::Symbol(name, anchor) => write!(f, "movw {}, #:lower16:{}-({}+8)", dest, name, anchor),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct MovtInstr(Register, MoveWideOperand);
|
||||||
|
impl MovtInstr {
|
||||||
|
pub fn new_imm(dest: Register, value: u16) -> ARMInstr {
|
||||||
|
ARMInstr::Movt(MovtInstr(dest, MoveWideOperand::Imm(value)))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_symbol(dest: Register, name: String, anchor: String) -> ARMInstr {
|
||||||
|
ARMInstr::Movt(MovtInstr(dest, MoveWideOperand::Symbol(name, anchor)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Display for MovtInstr {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let MovtInstr(dest, operand) = self;
|
||||||
|
match operand {
|
||||||
|
MoveWideOperand::Imm(value) => write!(f, "movt {}, #{}", dest, value),
|
||||||
|
MoveWideOperand::Symbol(name, anchor) => write!(f, "movt {}, #:upper16:{}-({}+8)", dest, name, anchor),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct LoadInstr(Register, Register, Option<RegisterOrImm>);
|
pub struct LoadInstr(Register, Register, Option<RegisterOrImm>);
|
||||||
impl LoadInstr {
|
impl LoadInstr {
|
||||||
pub fn new(dest: Register, base: Register, offset: Option<RegisterOrImm>) -> ARMInstr {
|
pub fn new(dest: Register, base: Register, offset: Option<RegisterOrImm>) -> ARMInstr {
|
||||||
@@ -146,18 +196,6 @@ impl Display for LoadInstr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub struct LoadPseudoInstr(Register, String);
|
|
||||||
impl LoadPseudoInstr {
|
|
||||||
pub fn new(dest: Register, name: String) -> ARMInstr {
|
|
||||||
ARMInstr::LoadPseudo(LoadPseudoInstr(dest, name))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Display for LoadPseudoInstr {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
let LoadPseudoInstr(dest, name) = self;
|
|
||||||
write!(f, "ldr {}, ={}", dest, name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub struct StoreInstr(Register, Register, Option<RegisterOrImm>);
|
pub struct StoreInstr(Register, Register, Option<RegisterOrImm>);
|
||||||
impl StoreInstr {
|
impl StoreInstr {
|
||||||
pub fn new(src: Register, dest: Register, offset: Option<RegisterOrImm>) -> ARMInstr {
|
pub fn new(src: Register, dest: Register, offset: Option<RegisterOrImm>) -> ARMInstr {
|
||||||
|
|||||||
+88
-44
@@ -1,6 +1,6 @@
|
|||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
use crate::{backend::{arm_instr::{ARMInstr, AddInstr, BInstr, BlInstr, CmpInstr, ConditionCode, LoadInstr, LoadPseudoInstr, MoveInstr, MulInstr, PopInstr, PushInstr, RegisterOrImm, RsbInstr, SDivInstr, StoreInstr, SubInstr}, register_allocator::{REG_FP, REG_R0, REG_R1, REG_R2, REG_R3, REG_SP, Register, RegisterAlloc, RegisterAllocator}, types::ARMAsmVar}, ir::types::{Function, IRInstr, MoveRValue, Variable, VariableOrIntLit, VariableType}};
|
use crate::{backend::{arm_instr::{ARMInstr, AddInstr, BInstr, BlInstr, CmpInstr, ConditionCode, LoadInstr, MoveInstr, MovtInstr, MovwInstr, MulInstr, PopInstr, PushInstr, RegisterOrImm, RsbInstr, SDivInstr, StoreInstr, SubInstr}, register_allocator::{REG_FP, REG_PC, REG_R0, REG_R1, REG_R2, REG_R3, REG_SP, Register, RegisterAlloc, RegisterAllocator}, types::ARMAsmVar}, ir::types::{Function, IRInstr, MoveRValue, Variable, VariableOrIntLit, VariableType}};
|
||||||
use crate::ir::types::BinaryOp as IRBinaryOp;
|
use crate::ir::types::BinaryOp as IRBinaryOp;
|
||||||
use crate::ir::types::CmpOp as IRCmpOp;
|
use crate::ir::types::CmpOp as IRCmpOp;
|
||||||
use crate::ir::types::UnaryOp as IRUnaryOp;
|
use crate::ir::types::UnaryOp as IRUnaryOp;
|
||||||
@@ -11,6 +11,7 @@ pub struct Generator {
|
|||||||
var_inited: Vec<ARMAsmVar>,
|
var_inited: Vec<ARMAsmVar>,
|
||||||
var_uninited: Vec<ARMAsmVar>,
|
var_uninited: Vec<ARMAsmVar>,
|
||||||
register_allocator: RegisterAllocator,
|
register_allocator: RegisterAllocator,
|
||||||
|
symbol_addr_label_counter: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
const DEFAULT_VAR_ALIGN: usize = 4;
|
const DEFAULT_VAR_ALIGN: usize = 4;
|
||||||
@@ -19,6 +20,17 @@ const ARG_REGS: [Register; 4] = [REG_R0, REG_R1, REG_R2, REG_R3];
|
|||||||
const ARM_DATA_IMM_CHUNK: i32 = 255;
|
const ARM_DATA_IMM_CHUNK: i32 = 255;
|
||||||
const ARM_LOAD_STORE_MAX_OFFSET: i32 = 4095;
|
const ARM_LOAD_STORE_MAX_OFFSET: i32 = 4095;
|
||||||
|
|
||||||
|
fn emit_load_imm32(dest: Register, value: i32, instrs: &mut Vec<ARMInstr>) {
|
||||||
|
let bits = value as u32;
|
||||||
|
let lower = (bits & 0xffff) as u16;
|
||||||
|
let upper = (bits >> 16) as u16;
|
||||||
|
|
||||||
|
instrs.push(MovwInstr::new_imm(dest, lower));
|
||||||
|
if upper != 0 {
|
||||||
|
instrs.push(MovtInstr::new_imm(dest, upper));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn emit_sub_imm(dest: Register, left: Register, imm: i32, instrs: &mut Vec<ARMInstr>) {
|
fn emit_sub_imm(dest: Register, left: Register, imm: i32, instrs: &mut Vec<ARMInstr>) {
|
||||||
if imm == 0 {
|
if imm == 0 {
|
||||||
if dest != left {
|
if dest != left {
|
||||||
@@ -75,16 +87,34 @@ fn emit_store_stack(src: Register, offset: i32, reg_allocator: &mut RegisterAllo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_variable(variable: Variable, reg_allocator: &mut RegisterAllocator, var_index_to_stack_offset: &BTreeMap<usize, usize>, instrs: &mut Vec<ARMInstr>) -> RegisterAlloc {
|
fn load_variable(variable: Variable, generator: &mut Generator, var_index_to_stack_offset: &BTreeMap<usize, usize>) -> RegisterAlloc {
|
||||||
|
let reg_allocator = &mut generator.register_allocator;
|
||||||
if variable.data_type.is_array() {
|
if variable.data_type.is_array() {
|
||||||
let var_alloc = reg_allocator.alloc(variable.clone()).expect("Ran out of registers");
|
let var_alloc = reg_allocator.alloc(variable.clone()).expect("Ran out of registers");
|
||||||
|
if variable.data_type.is_array_param() {
|
||||||
match variable.var_type {
|
match variable.var_type {
|
||||||
VariableType::Global => {
|
VariableType::ParamTemp(param_index) => {
|
||||||
instrs.push(LoadPseudoInstr::new(var_alloc.reg, format!("global_var_{}", variable.index)));
|
if param_index < ARG_REGS.len() {
|
||||||
|
generator.instrs.push(MoveInstr::new_uncond(var_alloc.reg, RegisterOrImm::Reg(ARG_REGS[param_index])));
|
||||||
|
} else {
|
||||||
|
let stack_arg_offset = 8 + ((param_index - ARG_REGS.len()) * 4);
|
||||||
|
generator.instrs.push(LoadInstr::new(var_alloc.reg, REG_FP, Some(RegisterOrImm::Imm(stack_arg_offset as i32))));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let stack_offset = var_index_to_stack_offset.get(&variable.index).expect("Variable not declared");
|
let stack_offset = var_index_to_stack_offset.get(&variable.index).expect("Variable not declared");
|
||||||
emit_sub_imm(var_alloc.reg, REG_FP, *stack_offset as i32, instrs);
|
emit_load_stack(var_alloc.reg, *stack_offset as i32, &mut generator.register_allocator, &mut generator.instrs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return var_alloc;
|
||||||
|
}
|
||||||
|
match variable.var_type {
|
||||||
|
VariableType::Global => {
|
||||||
|
generator.emit_load_symbol_addr(var_alloc.reg, format!("global_var_{}", variable.index));
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let stack_offset = var_index_to_stack_offset.get(&variable.index).expect("Variable not declared");
|
||||||
|
emit_sub_imm(var_alloc.reg, REG_FP, *stack_offset as i32, &mut generator.instrs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return var_alloc;
|
return var_alloc;
|
||||||
@@ -95,8 +125,8 @@ fn load_variable(variable: Variable, reg_allocator: &mut RegisterAllocator, var_
|
|||||||
let var_reg = var_alloc.reg;
|
let var_reg = var_alloc.reg;
|
||||||
// if !var_alloc.is_reused {
|
// if !var_alloc.is_reused {
|
||||||
let address_alloc = reg_allocator.alloc_any().expect("Ran out of registers");
|
let address_alloc = reg_allocator.alloc_any().expect("Ran out of registers");
|
||||||
instrs.push(LoadPseudoInstr::new(address_alloc.reg, format!("global_var_{}", variable.index)));
|
generator.emit_load_symbol_addr(address_alloc.reg, format!("global_var_{}", variable.index));
|
||||||
instrs.push(LoadInstr::new(var_reg, address_alloc.reg, None));
|
generator.instrs.push(LoadInstr::new(var_reg, address_alloc.reg, None));
|
||||||
// }
|
// }
|
||||||
var_alloc
|
var_alloc
|
||||||
},
|
},
|
||||||
@@ -108,7 +138,7 @@ fn load_variable(variable: Variable, reg_allocator: &mut RegisterAllocator, var_
|
|||||||
} else {
|
} else {
|
||||||
let var_alloc = reg_allocator.alloc(variable.clone()).expect("Ran out of registers");
|
let var_alloc = reg_allocator.alloc(variable.clone()).expect("Ran out of registers");
|
||||||
let stack_arg_offset = 8 + ((param_index - ARG_REGS.len()) * 4);
|
let stack_arg_offset = 8 + ((param_index - ARG_REGS.len()) * 4);
|
||||||
instrs.push(LoadInstr::new(var_alloc.reg, REG_FP, Some(RegisterOrImm::Imm(stack_arg_offset as i32))));
|
generator.instrs.push(LoadInstr::new(var_alloc.reg, REG_FP, Some(RegisterOrImm::Imm(stack_arg_offset as i32))));
|
||||||
var_alloc
|
var_alloc
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -116,26 +146,26 @@ fn load_variable(variable: Variable, reg_allocator: &mut RegisterAllocator, var_
|
|||||||
let stack_offset = var_index_to_stack_offset.get(&variable.index).expect("Variable not declared");
|
let stack_offset = var_index_to_stack_offset.get(&variable.index).expect("Variable not declared");
|
||||||
let var_alloc = reg_allocator.alloc(variable.clone()).expect("Ran out of registers");
|
let var_alloc = reg_allocator.alloc(variable.clone()).expect("Ran out of registers");
|
||||||
// if !var_alloc.is_reused {
|
// if !var_alloc.is_reused {
|
||||||
emit_load_stack(var_alloc.reg, *stack_offset as i32, reg_allocator, instrs);
|
emit_load_stack(var_alloc.reg, *stack_offset as i32, &mut generator.register_allocator, &mut generator.instrs);
|
||||||
// }
|
// }
|
||||||
var_alloc
|
var_alloc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn save_variable(variable: Variable, reg: Register, reg_allocator: &mut RegisterAllocator, var_index_to_stack_offset: &BTreeMap<usize, usize>, instrs: &mut Vec<ARMInstr>) {
|
fn save_variable(variable: Variable, reg: Register, generator: &mut Generator, var_index_to_stack_offset: &BTreeMap<usize, usize>) {
|
||||||
match variable.var_type {
|
match variable.var_type {
|
||||||
VariableType::Global => {
|
VariableType::Global => {
|
||||||
let address_alloc = reg_allocator.alloc_any().expect("Ran out of registers");
|
let address_alloc = generator.register_allocator.alloc_any().expect("Ran out of registers");
|
||||||
instrs.push(LoadPseudoInstr::new(address_alloc.reg, format!("global_var_{}", variable.index)));
|
generator.emit_load_symbol_addr(address_alloc.reg, format!("global_var_{}", variable.index));
|
||||||
instrs.push(StoreInstr::new(reg, address_alloc.reg, None));
|
generator.instrs.push(StoreInstr::new(reg, address_alloc.reg, None));
|
||||||
},
|
},
|
||||||
VariableType::ParamTemp(_) => {
|
VariableType::ParamTemp(_) => {
|
||||||
todo!()
|
todo!()
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
let stack_offset = var_index_to_stack_offset.get(&variable.index).expect("Variable not declared");
|
let stack_offset = var_index_to_stack_offset.get(&variable.index).expect("Variable not declared");
|
||||||
emit_store_stack(reg, *stack_offset as i32, reg_allocator, instrs);
|
emit_store_stack(reg, *stack_offset as i32, &mut generator.register_allocator, &mut generator.instrs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -146,13 +176,22 @@ impl Generator {
|
|||||||
var_inited: Vec::new(),
|
var_inited: Vec::new(),
|
||||||
var_uninited: Vec::new(),
|
var_uninited: Vec::new(),
|
||||||
register_allocator: RegisterAllocator::new(),
|
register_allocator: RegisterAllocator::new(),
|
||||||
|
symbol_addr_label_counter: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fn emit_load_symbol_addr(&mut self, dest: Register, name: String) {
|
||||||
|
let anchor = format!(".Laddr_{}", self.symbol_addr_label_counter);
|
||||||
|
self.symbol_addr_label_counter += 1;
|
||||||
|
self.instrs.push(MovwInstr::new_symbol(dest, name.clone(), anchor.clone()));
|
||||||
|
self.instrs.push(MovtInstr::new_symbol(dest, name, anchor.clone()));
|
||||||
|
self.instrs.push(ARMInstr::LocalLabel(anchor));
|
||||||
|
self.instrs.push(AddInstr::new(dest, REG_PC, RegisterOrImm::Reg(dest)));
|
||||||
|
}
|
||||||
pub fn emit(&mut self, ir_instrs: Vec<IRInstr>) {
|
pub fn emit(&mut self, ir_instrs: Vec<IRInstr>) {
|
||||||
for ir_instr in ir_instrs {
|
for ir_instr in ir_instrs {
|
||||||
match ir_instr {
|
match ir_instr {
|
||||||
IRInstr::DefineFunc(func, args, body) => self.emit_func_def(func, args, body),
|
IRInstr::DefineFunc(func, args, body) => self.emit_func_def(func, args, body),
|
||||||
IRInstr::Declare(var) => self.emit_global_decl(var),
|
IRInstr::DeclareGlobal(var, init) => self.emit_global_decl(var, init),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -164,8 +203,8 @@ impl Generator {
|
|||||||
text.push_str(&format!(".comm {}, {}, {}\n", var.name, var.size, var.align));
|
text.push_str(&format!(".comm {}, {}, {}\n", var.name, var.size, var.align));
|
||||||
}
|
}
|
||||||
for var in &self.var_inited {
|
for var in &self.var_inited {
|
||||||
text.push_str(&format!(".data\n.align {}\n.global {}\n.type {}, @object\n:{}\n", var.align, var.name, var.name, var.name));
|
text.push_str(&format!(".data\n.align {}\n.global {}\n.type {}, %object\n{}:\n", var.align, var.name, var.name, var.name));
|
||||||
text.push_str(&format!(".word 0\n"));
|
text.push_str(&format!(".word {}\n", var.init.unwrap_or(0)));
|
||||||
}
|
}
|
||||||
text.push_str(".text\n");
|
text.push_str(".text\n");
|
||||||
for instr in &self.instrs {
|
for instr in &self.instrs {
|
||||||
@@ -174,13 +213,18 @@ impl Generator {
|
|||||||
text
|
text
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn emit_global_decl(&mut self, var: Variable, init: Option<i32>) {
|
||||||
fn emit_global_decl(&mut self, var: Variable) {
|
let asm_var = ARMAsmVar {
|
||||||
self.var_uninited.push(ARMAsmVar {
|
|
||||||
name: format!("global_var_{}", var.index),
|
name: format!("global_var_{}", var.index),
|
||||||
size: var.data_type.size_in_bytes(),
|
size: var.data_type.size_in_bytes(),
|
||||||
align: DEFAULT_VAR_ALIGN,
|
align: DEFAULT_VAR_ALIGN,
|
||||||
});
|
init,
|
||||||
|
};
|
||||||
|
if asm_var.init.is_some() {
|
||||||
|
self.var_inited.push(asm_var);
|
||||||
|
} else {
|
||||||
|
self.var_uninited.push(asm_var);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emit_func_def(&mut self, func: Function, _args: Vec<Variable>, body: Vec<IRInstr>) {
|
fn emit_func_def(&mut self, func: Function, _args: Vec<Variable>, body: Vec<IRInstr>) {
|
||||||
@@ -201,7 +245,7 @@ impl Generator {
|
|||||||
if let Some(v) = v {
|
if let Some(v) = v {
|
||||||
let ret_alloc = self.register_allocator.alloc_reg(REG_R0).expect("Ran out of registers");
|
let ret_alloc = self.register_allocator.alloc_reg(REG_R0).expect("Ran out of registers");
|
||||||
let ret_reg = ret_alloc.reg;
|
let ret_reg = ret_alloc.reg;
|
||||||
let v_alloc = load_variable(v, &mut self.register_allocator, &var_index_to_stack_offset, &mut self.instrs);
|
let v_alloc = load_variable(v, self, &var_index_to_stack_offset);
|
||||||
self.instrs.push(MoveInstr::new_uncond(ret_reg, RegisterOrImm::Reg(v_alloc.reg)));
|
self.instrs.push(MoveInstr::new_uncond(ret_reg, RegisterOrImm::Reg(v_alloc.reg)));
|
||||||
}
|
}
|
||||||
self.instrs.push(MoveInstr::new_fp_to_sp());
|
self.instrs.push(MoveInstr::new_fp_to_sp());
|
||||||
@@ -213,7 +257,7 @@ impl Generator {
|
|||||||
IRInstr::Store(addr, value) => self.emit_store(addr, value, &var_index_to_stack_offset),
|
IRInstr::Store(addr, value) => self.emit_store(addr, value, &var_index_to_stack_offset),
|
||||||
IRInstr::Declare(variable) => {
|
IRInstr::Declare(variable) => {
|
||||||
assert!(!encounter_entry, "Variable declarations must come before entry instruction");
|
assert!(!encounter_entry, "Variable declarations must come before entry instruction");
|
||||||
let size = variable.data_type.size_in_bytes();
|
let size = if variable.data_type.is_array_param() { 4 } else { variable.data_type.size_in_bytes() };
|
||||||
stack_size_needed = (stack_size_needed + size).next_multiple_of(ARM_STACK_ALIGNMENT);
|
stack_size_needed = (stack_size_needed + size).next_multiple_of(ARM_STACK_ALIGNMENT);
|
||||||
var_index_to_stack_offset.insert(variable.index, stack_size_needed);
|
var_index_to_stack_offset.insert(variable.index, stack_size_needed);
|
||||||
},
|
},
|
||||||
@@ -222,12 +266,12 @@ impl Generator {
|
|||||||
encounter_entry = true;
|
encounter_entry = true;
|
||||||
emit_sub_sp_imm(stack_size_needed as i32, &mut self.instrs);
|
emit_sub_sp_imm(stack_size_needed as i32, &mut self.instrs);
|
||||||
},
|
},
|
||||||
IRInstr::DefineFunc(_, _, _) => unreachable!(),
|
IRInstr::DefineFunc(_, _, _) | IRInstr::DeclareGlobal(_, _) => unreachable!(),
|
||||||
IRInstr::Cmp(variable, left, cmp_op, right) => self.emit_cmp(variable, left, cmp_op, right, &var_index_to_stack_offset),
|
IRInstr::Cmp(variable, left, cmp_op, right) => self.emit_cmp(variable, left, cmp_op, right, &var_index_to_stack_offset),
|
||||||
IRInstr::Unary(variable, unary_op, variable1) => self.emit_unary(variable, unary_op, variable1, &var_index_to_stack_offset),
|
IRInstr::Unary(variable, unary_op, variable1) => self.emit_unary(variable, unary_op, variable1, &var_index_to_stack_offset),
|
||||||
IRInstr::Goto(index) => self.instrs.push(BInstr::new(format!("label_{}_{}", func_name, index))),
|
IRInstr::Goto(index) => self.instrs.push(BInstr::new(format!("label_{}_{}", func_name, index))),
|
||||||
IRInstr::CondGoto(variable, true_label_index, false_label_index) => {
|
IRInstr::CondGoto(variable, true_label_index, false_label_index) => {
|
||||||
let variable_alloc = load_variable(variable, &mut self.register_allocator, &var_index_to_stack_offset, &mut self.instrs);
|
let variable_alloc = load_variable(variable, self, &var_index_to_stack_offset);
|
||||||
let variable_reg = variable_alloc.reg;
|
let variable_reg = variable_alloc.reg;
|
||||||
self.instrs.push(CmpInstr::new(variable_reg, RegisterOrImm::Imm(0)));
|
self.instrs.push(CmpInstr::new(variable_reg, RegisterOrImm::Imm(0)));
|
||||||
self.instrs.push(BInstr::new_cond(ConditionCode::Ne, format!("label_{}_{}", func_name, true_label_index)));
|
self.instrs.push(BInstr::new_cond(ConditionCode::Ne, format!("label_{}_{}", func_name, true_label_index)));
|
||||||
@@ -239,34 +283,34 @@ impl Generator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn emit_unary(&mut self, dest: Variable, unary_op: IRUnaryOp, variable: Variable, var_index_to_stack_offset: &BTreeMap<usize, usize>) {
|
fn emit_unary(&mut self, dest: Variable, unary_op: IRUnaryOp, variable: Variable, var_index_to_stack_offset: &BTreeMap<usize, usize>) {
|
||||||
let variable_alloc = load_variable(variable, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs);
|
let variable_alloc = load_variable(variable, self, var_index_to_stack_offset);
|
||||||
let dest_alloc = self.register_allocator.alloc(dest.clone()).expect("Ran out of registers");
|
let dest_alloc = self.register_allocator.alloc(dest.clone()).expect("Ran out of registers");
|
||||||
match unary_op {
|
match unary_op {
|
||||||
IRUnaryOp::Neg => {
|
IRUnaryOp::Neg => {
|
||||||
self.instrs.push(RsbInstr::new(dest_alloc.reg, variable_alloc.reg, RegisterOrImm::Imm(0)));
|
self.instrs.push(RsbInstr::new(dest_alloc.reg, variable_alloc.reg, RegisterOrImm::Imm(0)));
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
save_variable(dest, dest_alloc.reg, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs);
|
save_variable(dest, dest_alloc.reg, self, var_index_to_stack_offset);
|
||||||
}
|
}
|
||||||
fn emit_cmp(&mut self, variable: Variable, left: VariableOrIntLit, cmp_op: IRCmpOp, right: VariableOrIntLit, var_index_to_stack_offset: &BTreeMap<usize, usize>) {
|
fn emit_cmp(&mut self, variable: Variable, left: VariableOrIntLit, cmp_op: IRCmpOp, right: VariableOrIntLit, var_index_to_stack_offset: &BTreeMap<usize, usize>) {
|
||||||
let left_alloc = match left {
|
let left_alloc = match left {
|
||||||
VariableOrIntLit::Var(var) => {
|
VariableOrIntLit::Var(var) => {
|
||||||
load_variable(var, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs)
|
load_variable(var, self, var_index_to_stack_offset)
|
||||||
},
|
},
|
||||||
VariableOrIntLit::IntLit(lit) => {
|
VariableOrIntLit::IntLit(lit) => {
|
||||||
let alloc = self.register_allocator.alloc_any().expect("Ran out of registers");
|
let alloc = self.register_allocator.alloc_any().expect("Ran out of registers");
|
||||||
self.instrs.push(MoveInstr::new_uncond(alloc.reg, RegisterOrImm::Imm(lit)));
|
emit_load_imm32(alloc.reg, lit, &mut self.instrs);
|
||||||
alloc
|
alloc
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
// TODO: check left == right
|
// TODO: check left == right
|
||||||
let right_alloc = match right {
|
let right_alloc = match right {
|
||||||
VariableOrIntLit::Var(var) => {
|
VariableOrIntLit::Var(var) => {
|
||||||
load_variable(var, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs)
|
load_variable(var, self, var_index_to_stack_offset)
|
||||||
},
|
},
|
||||||
VariableOrIntLit::IntLit(lit) => {
|
VariableOrIntLit::IntLit(lit) => {
|
||||||
let alloc = self.register_allocator.alloc_any().expect("Ran out of registers");
|
let alloc = self.register_allocator.alloc_any().expect("Ran out of registers");
|
||||||
self.instrs.push(MoveInstr::new_uncond(alloc.reg, RegisterOrImm::Imm(lit)));
|
emit_load_imm32(alloc.reg, lit, &mut self.instrs);
|
||||||
alloc
|
alloc
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -275,7 +319,7 @@ impl Generator {
|
|||||||
self.instrs.push(CmpInstr::new(left_alloc.reg, RegisterOrImm::Reg(right_alloc.reg)));
|
self.instrs.push(CmpInstr::new(left_alloc.reg, RegisterOrImm::Reg(right_alloc.reg)));
|
||||||
self.instrs.push(MoveInstr::new_uncond(variable_reg, RegisterOrImm::Imm(0)));
|
self.instrs.push(MoveInstr::new_uncond(variable_reg, RegisterOrImm::Imm(0)));
|
||||||
self.instrs.push(MoveInstr::new_cond(cmp_op.into(), variable_reg, RegisterOrImm::Imm(1)));
|
self.instrs.push(MoveInstr::new_cond(cmp_op.into(), variable_reg, RegisterOrImm::Imm(1)));
|
||||||
save_variable(variable, variable_alloc.reg, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs);
|
save_variable(variable, variable_alloc.reg, self, var_index_to_stack_offset);
|
||||||
}
|
}
|
||||||
fn emit_func_call(&mut self, func: Function, args: Vec<Variable>, ret: Option<Variable>, var_index_to_stack_offset: &BTreeMap<usize, usize>) {
|
fn emit_func_call(&mut self, func: Function, args: Vec<Variable>, ret: Option<Variable>, var_index_to_stack_offset: &BTreeMap<usize, usize>) {
|
||||||
|
|
||||||
@@ -290,7 +334,7 @@ impl Generator {
|
|||||||
}
|
}
|
||||||
let mut arg_reg_allocs = Vec::new();
|
let mut arg_reg_allocs = Vec::new();
|
||||||
for (i, arg) in args.into_iter().enumerate() {
|
for (i, arg) in args.into_iter().enumerate() {
|
||||||
let arg_alloc = load_variable(arg, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs);
|
let arg_alloc = load_variable(arg, self, var_index_to_stack_offset);
|
||||||
if i < ARG_REGS.len() {
|
if i < ARG_REGS.len() {
|
||||||
arg_reg_allocs.push(self.register_allocator.alloc_reg(ARG_REGS[i]).expect("Ran out of registers"));
|
arg_reg_allocs.push(self.register_allocator.alloc_reg(ARG_REGS[i]).expect("Ran out of registers"));
|
||||||
self.instrs.push(MoveInstr::new_uncond(ARG_REGS[i], RegisterOrImm::Reg(arg_alloc.reg)));
|
self.instrs.push(MoveInstr::new_uncond(ARG_REGS[i], RegisterOrImm::Reg(arg_alloc.reg)));
|
||||||
@@ -301,7 +345,7 @@ impl Generator {
|
|||||||
}
|
}
|
||||||
self.instrs.push(BlInstr::new(func.name.clone()));
|
self.instrs.push(BlInstr::new(func.name.clone()));
|
||||||
if let Some(ret) = ret {
|
if let Some(ret) = ret {
|
||||||
save_variable(ret, REG_R0, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs);
|
save_variable(ret, REG_R0, self, var_index_to_stack_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
if extra_stack_size > 0 {
|
if extra_stack_size > 0 {
|
||||||
@@ -315,24 +359,24 @@ impl Generator {
|
|||||||
let dest_register = dest_alloc.reg;
|
let dest_register = dest_alloc.reg;
|
||||||
match src {
|
match src {
|
||||||
MoveRValue::Var(variable) => {
|
MoveRValue::Var(variable) => {
|
||||||
let src_alloc = load_variable(variable, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs);
|
let src_alloc = load_variable(variable, self, var_index_to_stack_offset);
|
||||||
self.instrs.push(MoveInstr::new_uncond(dest_register, RegisterOrImm::Reg(src_alloc.reg)));
|
self.instrs.push(MoveInstr::new_uncond(dest_register, RegisterOrImm::Reg(src_alloc.reg)));
|
||||||
},
|
},
|
||||||
MoveRValue::ConstInt(literal_int) => self.instrs.push(MoveInstr::new_uncond(dest_register, RegisterOrImm::Imm(literal_int))),
|
MoveRValue::ConstInt(literal_int) => emit_load_imm32(dest_register, literal_int, &mut self.instrs),
|
||||||
};
|
};
|
||||||
save_variable(dest, dest_alloc.reg, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs);
|
save_variable(dest, dest_alloc.reg, self, var_index_to_stack_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emit_load(&mut self, dest: Variable, addr: Variable, var_index_to_stack_offset: &BTreeMap<usize, usize>) {
|
fn emit_load(&mut self, dest: Variable, addr: Variable, var_index_to_stack_offset: &BTreeMap<usize, usize>) {
|
||||||
let addr_alloc = load_variable(addr, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs);
|
let addr_alloc = load_variable(addr, self, var_index_to_stack_offset);
|
||||||
let dest_alloc = self.register_allocator.alloc(dest.clone()).expect("Ran out of registers");
|
let dest_alloc = self.register_allocator.alloc(dest.clone()).expect("Ran out of registers");
|
||||||
self.instrs.push(LoadInstr::new(dest_alloc.reg, addr_alloc.reg, None));
|
self.instrs.push(LoadInstr::new(dest_alloc.reg, addr_alloc.reg, None));
|
||||||
save_variable(dest, dest_alloc.reg, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs);
|
save_variable(dest, dest_alloc.reg, self, var_index_to_stack_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emit_store(&mut self, addr: Variable, value: Variable, var_index_to_stack_offset: &BTreeMap<usize, usize>) {
|
fn emit_store(&mut self, addr: Variable, value: Variable, var_index_to_stack_offset: &BTreeMap<usize, usize>) {
|
||||||
let addr_alloc = load_variable(addr, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs);
|
let addr_alloc = load_variable(addr, self, var_index_to_stack_offset);
|
||||||
let value_alloc = load_variable(value, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs);
|
let value_alloc = load_variable(value, self, var_index_to_stack_offset);
|
||||||
self.instrs.push(StoreInstr::new(value_alloc.reg, addr_alloc.reg, None));
|
self.instrs.push(StoreInstr::new(value_alloc.reg, addr_alloc.reg, None));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -340,9 +384,9 @@ impl Generator {
|
|||||||
let dest_alloc = self.register_allocator.alloc(dest.clone()).expect("Ran out of registers");
|
let dest_alloc = self.register_allocator.alloc(dest.clone()).expect("Ran out of registers");
|
||||||
let dest_reg = dest_alloc.reg;
|
let dest_reg = dest_alloc.reg;
|
||||||
// should consider left == right
|
// should consider left == right
|
||||||
let left_alloc = load_variable(left.clone(), &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs);
|
let left_alloc = load_variable(left.clone(), self, var_index_to_stack_offset);
|
||||||
let (_right_alloc, right_reg) = if left != right {
|
let (_right_alloc, right_reg) = if left != right {
|
||||||
let right_alloc = load_variable(right, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs);
|
let right_alloc = load_variable(right, self, var_index_to_stack_offset);
|
||||||
let right_reg = right_alloc.reg;
|
let right_reg = right_alloc.reg;
|
||||||
(Some(right_alloc), right_reg)
|
(Some(right_alloc), right_reg)
|
||||||
} else {
|
} else {
|
||||||
@@ -369,7 +413,7 @@ impl Generator {
|
|||||||
self.instrs.push(SubInstr::new(dest_reg, left_alloc.reg, RegisterOrImm::Reg(temp_reg)));
|
self.instrs.push(SubInstr::new(dest_reg, left_alloc.reg, RegisterOrImm::Reg(temp_reg)));
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
save_variable(dest, dest_alloc.reg, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs);
|
save_variable(dest, dest_alloc.reg, self, var_index_to_stack_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -10,8 +10,8 @@ mod tests {
|
|||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use crate::frontend::lexer::Lexer;
|
use crate::ast::parser::Parser;
|
||||||
use crate::frontend::parser::Parser;
|
use crate::lexer::lexer::Lexer;
|
||||||
use crate::utils::case_list::CaseList;
|
use crate::utils::case_list::CaseList;
|
||||||
use crate::utils::num_sequence::NumberSequence;
|
use crate::utils::num_sequence::NumberSequence;
|
||||||
use crate::ir::generator::Generator as IRGenerator;
|
use crate::ir::generator::Generator as IRGenerator;
|
||||||
|
|||||||
@@ -2,4 +2,5 @@ pub struct ARMAsmVar {
|
|||||||
pub name: String,
|
pub name: String,
|
||||||
pub size: usize,
|
pub size: usize,
|
||||||
pub align: usize,
|
pub align: usize,
|
||||||
|
pub init: Option<i32>,
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::{diagnostic::span::Span, err::CompileError, frontend::err::FrontendError, ir::err::IRError};
|
use crate::{diagnostic::span::Span, err::CompileError, ir::err::IRError, lexer::err::FrontendError};
|
||||||
|
|
||||||
pub mod span;
|
pub mod span;
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::{frontend::err::FrontendError, sema::err::SemaError};
|
use crate::{lexer::err::FrontendError, sema::err::SemaError};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Error)]
|
#[derive(Debug, Clone, PartialEq, Eq, Error)]
|
||||||
pub enum CompileError {
|
pub enum CompileError {
|
||||||
|
|||||||
@@ -1,40 +0,0 @@
|
|||||||
use thiserror::Error;
|
|
||||||
|
|
||||||
use crate::frontend::types::{Token, TokenValue};
|
|
||||||
|
|
||||||
// #[derive(Debug, Clone, PartialEq, Eq, Error)]
|
|
||||||
// pub enum ParseError {
|
|
||||||
// BlockStmt(#[from] BlockStmtError)
|
|
||||||
// }
|
|
||||||
// #[derive(Debug, Clone, PartialEq, Eq, Error)]
|
|
||||||
// pub enum BlockStmtError {
|
|
||||||
// MissingLBrace,
|
|
||||||
// MissingRBrace,
|
|
||||||
// }
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Error)]
|
|
||||||
pub enum LexingError {
|
|
||||||
#[error("invalid int literal")]
|
|
||||||
InvalidIntLiteral,
|
|
||||||
#[error("invalid ident")]
|
|
||||||
InvalidIdent,
|
|
||||||
#[error("comment unterminated")]
|
|
||||||
UnterminatedComment,
|
|
||||||
#[error("unrecognized token: {0}")]
|
|
||||||
UnrecognizedToken(String),
|
|
||||||
}
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Error)]
|
|
||||||
pub enum ParseError {
|
|
||||||
#[error("unexpected token {}, expect {}", .0, .1)]
|
|
||||||
UnexpectedToken(TokenValue, &'static str),
|
|
||||||
#[error("cannot combine with previous {}", .0)]
|
|
||||||
CantCombineWith(TokenValue),
|
|
||||||
#[error("expect {0} after")]
|
|
||||||
ExpectButEof(&'static str),
|
|
||||||
}
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Error)]
|
|
||||||
pub enum FrontendError {
|
|
||||||
#[error(transparent)]
|
|
||||||
Lexing(#[from] LexingError),
|
|
||||||
#[error(transparent)]
|
|
||||||
Parse(#[from] ParseError),
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
+446
@@ -0,0 +1,446 @@
|
|||||||
|
use std::collections::{BTreeMap, BTreeSet, HashSet};
|
||||||
|
|
||||||
|
use petgraph::dot::Dot;
|
||||||
|
use petgraph::graph::Graph;
|
||||||
|
|
||||||
|
use crate::ir::types::IRInstr;
|
||||||
|
|
||||||
|
pub type CfgGraph = Graph<String, String>;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct BasicBlock {
|
||||||
|
pub id: usize,
|
||||||
|
pub instrs: Vec<IRInstr>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ControlFlowGraph {
|
||||||
|
pub func_name: String,
|
||||||
|
pub blocks: Vec<BasicBlock>,
|
||||||
|
pub edges: Vec<(usize, usize, String)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||||
|
enum EdgeKind {
|
||||||
|
Branch,
|
||||||
|
Fallthrough,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ControlFlowGraph {
|
||||||
|
pub fn from_body(func_name: impl Into<String>, body: &[IRInstr]) -> Self {
|
||||||
|
let blocks = split_basic_blocks(body);
|
||||||
|
let edges = collect_edges(&blocks)
|
||||||
|
.into_iter()
|
||||||
|
.map(|(from, to, label, _)| (from, to, label))
|
||||||
|
.collect();
|
||||||
|
Self {
|
||||||
|
func_name: func_name.into(),
|
||||||
|
blocks,
|
||||||
|
edges,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_to_graph(&self, graph: &mut CfgGraph) {
|
||||||
|
let mut nodes = Vec::new();
|
||||||
|
for block in &self.blocks {
|
||||||
|
nodes.push(graph.add_node(block_dot_label(&self.func_name, block)));
|
||||||
|
}
|
||||||
|
for (from, to, label) in &self.edges {
|
||||||
|
if let (Some(from), Some(to)) = (nodes.get(*from), nodes.get(*to)) {
|
||||||
|
graph.add_edge(*from, *to, label.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn refresh_edges(&mut self) {
|
||||||
|
self.edges = collect_edges(&self.blocks)
|
||||||
|
.into_iter()
|
||||||
|
.map(|(from, to, label, _)| (from, to, label))
|
||||||
|
.collect();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_unreachable(&mut self) -> bool {
|
||||||
|
if self.blocks.is_empty() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let edges = collect_edges(&self.blocks);
|
||||||
|
let mut adjacency = vec![Vec::new(); self.blocks.len()];
|
||||||
|
for (from, to, _, _) in edges {
|
||||||
|
adjacency[from].push(to);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut reachable = vec![false; self.blocks.len()];
|
||||||
|
let mut stack = vec![0];
|
||||||
|
while let Some(index) = stack.pop() {
|
||||||
|
if reachable[index] {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
reachable[index] = true;
|
||||||
|
for next in &adjacency[index] {
|
||||||
|
stack.push(*next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let old_len = self.blocks.len();
|
||||||
|
self.blocks = self
|
||||||
|
.blocks
|
||||||
|
.drain(..)
|
||||||
|
.enumerate()
|
||||||
|
.filter_map(|(index, block)| reachable[index].then_some(block))
|
||||||
|
.collect();
|
||||||
|
let changed = self.blocks.len() != old_len;
|
||||||
|
if changed {
|
||||||
|
self.refresh_edges();
|
||||||
|
}
|
||||||
|
changed
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_trivial_blocks(&mut self) -> bool {
|
||||||
|
if self.blocks.is_empty() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let edges = collect_edges(&self.blocks);
|
||||||
|
let explicit_pred_count = explicit_predecessor_counts(self.blocks.len(), &edges);
|
||||||
|
let mut redirect = BTreeMap::new();
|
||||||
|
let mut remove = HashSet::new();
|
||||||
|
let mut fallthrough_gotos = Vec::new();
|
||||||
|
|
||||||
|
for (index, block) in self.blocks.iter().enumerate() {
|
||||||
|
if let Some((label, target)) = jump_only_block(block) {
|
||||||
|
let target = resolve_label(target, &redirect);
|
||||||
|
redirect.insert(label, target);
|
||||||
|
for (from, to, _, kind) in &edges {
|
||||||
|
if *to == index && *kind == EdgeKind::Fallthrough {
|
||||||
|
fallthrough_gotos.push((*from, target));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
remove.insert(index);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(label) = label_only_block(block) {
|
||||||
|
if let Some(next_index) = next_kept_index(index, self.blocks.len(), &remove) {
|
||||||
|
if let Some(next_label) = first_label(&self.blocks[next_index]) {
|
||||||
|
redirect.insert(label, resolve_label(next_label, &redirect));
|
||||||
|
remove.insert(index);
|
||||||
|
} else if explicit_pred_count[index] == 0 {
|
||||||
|
remove.insert(index);
|
||||||
|
}
|
||||||
|
} else if explicit_pred_count[index] == 0 {
|
||||||
|
remove.insert(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if redirect.is_empty() && remove.is_empty() && fallthrough_gotos.is_empty() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (from, target) in fallthrough_gotos {
|
||||||
|
if !remove.contains(&from) && !ends_with_terminal(&self.blocks[from]) {
|
||||||
|
self.blocks[from].instrs.push(IRInstr::Goto(target));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for block in &mut self.blocks {
|
||||||
|
rewrite_targets(&mut block.instrs, &redirect);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.blocks = self
|
||||||
|
.blocks
|
||||||
|
.drain(..)
|
||||||
|
.enumerate()
|
||||||
|
.filter_map(|(index, block)| (!remove.contains(&index)).then_some(block))
|
||||||
|
.collect();
|
||||||
|
self.refresh_edges();
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn merge_linear_blocks(&mut self) -> bool {
|
||||||
|
let edges = collect_edges(&self.blocks);
|
||||||
|
let pred_count = predecessor_counts(self.blocks.len(), &edges);
|
||||||
|
|
||||||
|
let mut index = 0;
|
||||||
|
while index + 1 < self.blocks.len() {
|
||||||
|
let successors = edges
|
||||||
|
.iter()
|
||||||
|
.filter_map(|(from, to, _, _)| (*from == index).then_some(*to))
|
||||||
|
.collect::<BTreeSet<_>>();
|
||||||
|
|
||||||
|
if successors.len() == 1 {
|
||||||
|
let successor = *successors.iter().next().unwrap();
|
||||||
|
if successor == index + 1 && pred_count[successor] == 1 {
|
||||||
|
if matches!(self.blocks[index].instrs.last(), Some(IRInstr::Goto(_))) {
|
||||||
|
self.blocks[index].instrs.pop();
|
||||||
|
}
|
||||||
|
let successor_block = self.blocks.remove(successor);
|
||||||
|
self.blocks[index].instrs.extend(successor_block.instrs);
|
||||||
|
self.refresh_edges();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
index += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_instrs(self) -> Vec<IRInstr> {
|
||||||
|
self.blocks
|
||||||
|
.into_iter()
|
||||||
|
.flat_map(|block| block.instrs)
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn optimize_ir(ir: Vec<IRInstr>) -> Vec<IRInstr> {
|
||||||
|
ir.into_iter()
|
||||||
|
.map(|instr| match instr {
|
||||||
|
IRInstr::DefineFunc(func, args, body) => {
|
||||||
|
let func_name = func.name.clone();
|
||||||
|
IRInstr::DefineFunc(func, args, optimize_body(&func_name, body))
|
||||||
|
}
|
||||||
|
other => other,
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ir_to_cfg_dot(ir: &[IRInstr]) -> String {
|
||||||
|
let mut graph = Graph::new();
|
||||||
|
for instr in ir {
|
||||||
|
if let IRInstr::DefineFunc(func, _, body) = instr {
|
||||||
|
ControlFlowGraph::from_body(func.name.clone(), body).add_to_graph(&mut graph);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
format!("{}", Dot::new(&graph))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn optimize_body(func_name: &str, body: Vec<IRInstr>) -> Vec<IRInstr> {
|
||||||
|
let (mut prologue, cfg_body) = split_function_prologue(body);
|
||||||
|
if cfg_body.is_empty() {
|
||||||
|
return prologue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut cfg = ControlFlowGraph::from_body(func_name, &cfg_body);
|
||||||
|
cfg.remove_unreachable();
|
||||||
|
loop {
|
||||||
|
let mut changed = false;
|
||||||
|
changed |= cfg.remove_trivial_blocks();
|
||||||
|
changed |= cfg.remove_unreachable();
|
||||||
|
changed |= cfg.merge_linear_blocks();
|
||||||
|
if !changed {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut body = cfg.into_instrs();
|
||||||
|
remove_redundant_fallthrough_gotos(&mut body);
|
||||||
|
remove_unused_labels(&mut body);
|
||||||
|
prologue.extend(body);
|
||||||
|
prologue
|
||||||
|
}
|
||||||
|
|
||||||
|
fn split_function_prologue(mut body: Vec<IRInstr>) -> (Vec<IRInstr>, Vec<IRInstr>) {
|
||||||
|
if let Some(entry_index) = body.iter().position(|instr| matches!(instr, IRInstr::Entry)) {
|
||||||
|
let cfg_body = body.split_off(entry_index + 1);
|
||||||
|
(body, cfg_body)
|
||||||
|
} else {
|
||||||
|
(Vec::new(), body)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn split_basic_blocks(instrs: &[IRInstr]) -> Vec<BasicBlock> {
|
||||||
|
if instrs.is_empty() {
|
||||||
|
return Vec::new();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut leaders = BTreeSet::new();
|
||||||
|
leaders.insert(0);
|
||||||
|
for (index, instr) in instrs.iter().enumerate() {
|
||||||
|
if matches!(instr, IRInstr::Label(_)) {
|
||||||
|
leaders.insert(index);
|
||||||
|
}
|
||||||
|
if is_terminal(instr) && index + 1 < instrs.len() {
|
||||||
|
leaders.insert(index + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let leader_list = leaders.into_iter().collect::<Vec<_>>();
|
||||||
|
let mut blocks = Vec::new();
|
||||||
|
for (block_id, start) in leader_list.iter().enumerate() {
|
||||||
|
let end = leader_list
|
||||||
|
.get(block_id + 1)
|
||||||
|
.copied()
|
||||||
|
.unwrap_or(instrs.len());
|
||||||
|
blocks.push(BasicBlock {
|
||||||
|
id: block_id,
|
||||||
|
instrs: instrs[*start..end].to_vec(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
blocks
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collect_edges(blocks: &[BasicBlock]) -> Vec<(usize, usize, String, EdgeKind)> {
|
||||||
|
let label_to_block = label_to_block(blocks);
|
||||||
|
let mut edges = Vec::new();
|
||||||
|
|
||||||
|
for (index, block) in blocks.iter().enumerate() {
|
||||||
|
match block.instrs.last() {
|
||||||
|
Some(IRInstr::Goto(label)) => {
|
||||||
|
if let Some(target) = label_to_block.get(label) {
|
||||||
|
edges.push((index, *target, "br".to_string(), EdgeKind::Branch));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(IRInstr::CondGoto(_, true_label, false_label)) => {
|
||||||
|
if let Some(target) = label_to_block.get(true_label) {
|
||||||
|
edges.push((index, *target, "true".to_string(), EdgeKind::Branch));
|
||||||
|
}
|
||||||
|
if let Some(target) = label_to_block.get(false_label) {
|
||||||
|
edges.push((index, *target, "false".to_string(), EdgeKind::Branch));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(IRInstr::Exit(_)) => {}
|
||||||
|
Some(_) | None => {
|
||||||
|
if index + 1 < blocks.len() {
|
||||||
|
edges.push((index, index + 1, "fallthrough".to_string(), EdgeKind::Fallthrough));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
edges
|
||||||
|
}
|
||||||
|
|
||||||
|
fn label_to_block(blocks: &[BasicBlock]) -> BTreeMap<usize, usize> {
|
||||||
|
let mut map = BTreeMap::new();
|
||||||
|
for (index, block) in blocks.iter().enumerate() {
|
||||||
|
for instr in &block.instrs {
|
||||||
|
if let IRInstr::Label(label) = instr {
|
||||||
|
map.insert(*label, index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
map
|
||||||
|
}
|
||||||
|
|
||||||
|
fn predecessor_counts(block_count: usize, edges: &[(usize, usize, String, EdgeKind)]) -> Vec<usize> {
|
||||||
|
let mut counts = vec![0; block_count];
|
||||||
|
for (_, to, _, _) in edges {
|
||||||
|
counts[*to] += 1;
|
||||||
|
}
|
||||||
|
counts
|
||||||
|
}
|
||||||
|
|
||||||
|
fn explicit_predecessor_counts(block_count: usize, edges: &[(usize, usize, String, EdgeKind)]) -> Vec<usize> {
|
||||||
|
let mut counts = vec![0; block_count];
|
||||||
|
for (_, to, _, kind) in edges {
|
||||||
|
if *kind == EdgeKind::Branch {
|
||||||
|
counts[*to] += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
counts
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_kept_index(index: usize, block_count: usize, remove: &HashSet<usize>) -> Option<usize> {
|
||||||
|
(index + 1..block_count).find(|candidate| !remove.contains(candidate))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn first_label(block: &BasicBlock) -> Option<usize> {
|
||||||
|
match block.instrs.first() {
|
||||||
|
Some(IRInstr::Label(label)) => Some(*label),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn label_only_block(block: &BasicBlock) -> Option<usize> {
|
||||||
|
match block.instrs.as_slice() {
|
||||||
|
[IRInstr::Label(label)] => Some(*label),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn jump_only_block(block: &BasicBlock) -> Option<(usize, usize)> {
|
||||||
|
match block.instrs.as_slice() {
|
||||||
|
[IRInstr::Label(label), IRInstr::Goto(target)] => Some((*label, *target)),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_terminal(instr: &IRInstr) -> bool {
|
||||||
|
matches!(instr, IRInstr::Goto(_) | IRInstr::CondGoto(_, _, _) | IRInstr::Exit(_))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ends_with_terminal(block: &BasicBlock) -> bool {
|
||||||
|
block.instrs.last().is_some_and(is_terminal)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rewrite_targets(instrs: &mut [IRInstr], redirect: &BTreeMap<usize, usize>) {
|
||||||
|
for instr in instrs {
|
||||||
|
match instr {
|
||||||
|
IRInstr::Goto(label) => *label = resolve_label(*label, redirect),
|
||||||
|
IRInstr::CondGoto(_, true_label, false_label) => {
|
||||||
|
*true_label = resolve_label(*true_label, redirect);
|
||||||
|
*false_label = resolve_label(*false_label, redirect);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_label(label: usize, redirect: &BTreeMap<usize, usize>) -> usize {
|
||||||
|
let mut current = label;
|
||||||
|
let mut visited = HashSet::new();
|
||||||
|
while let Some(next) = redirect.get(¤t) {
|
||||||
|
if !visited.insert(current) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
current = *next;
|
||||||
|
}
|
||||||
|
current
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_unused_labels(instrs: &mut Vec<IRInstr>) {
|
||||||
|
let mut used = BTreeSet::new();
|
||||||
|
for instr in instrs.iter() {
|
||||||
|
match instr {
|
||||||
|
IRInstr::Goto(label) => {
|
||||||
|
used.insert(*label);
|
||||||
|
}
|
||||||
|
IRInstr::CondGoto(_, true_label, false_label) => {
|
||||||
|
used.insert(*true_label);
|
||||||
|
used.insert(*false_label);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
instrs.retain(|instr| match instr {
|
||||||
|
IRInstr::Label(label) => used.contains(label),
|
||||||
|
_ => true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_redundant_fallthrough_gotos(instrs: &mut Vec<IRInstr>) {
|
||||||
|
let mut index = 0;
|
||||||
|
while index + 1 < instrs.len() {
|
||||||
|
let remove = match (&instrs[index], &instrs[index + 1]) {
|
||||||
|
(IRInstr::Goto(target), IRInstr::Label(label)) => target == label,
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
if remove {
|
||||||
|
instrs.remove(index);
|
||||||
|
} else {
|
||||||
|
index += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn block_dot_label(func_name: &str, block: &BasicBlock) -> String {
|
||||||
|
let instrs = block
|
||||||
|
.instrs
|
||||||
|
.iter()
|
||||||
|
.map(|instr| instr.to_string())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join("\\l");
|
||||||
|
format!("{}::B{}\\l{}\\l", func_name, block.id, instrs)
|
||||||
|
}
|
||||||
+291
-26
@@ -1,6 +1,6 @@
|
|||||||
use std::{collections::BTreeMap, vec};
|
use std::{collections::BTreeMap, vec};
|
||||||
|
|
||||||
use crate::{ir::types::{CmpOp, Function, IRInstr, IRType, MoveRValue, UnaryOp, Variable, VariableOrIntLit, VariableType}, sema::{analyzer::Analyzer as SemaAnalyzer, hir::{HirBlockStmt, HirBreakStmt, HirCompileUnit, HirContinueStmt, HirExpr, HirExprValue, HirFuncDeclStmt, HirGlobalDeclStmt, HirIfElseBranch, HirIfStmt, HirReturnStmt, HirStatement, HirVarDeclStmt, HirWhileStmt}, symbol::{FunctionId, SymbolId}}};
|
use crate::{ir::types::{BinaryOp as IRBinaryOp, CmpOp, Function, IRInstr, IRType, MoveRValue, UnaryOp, Variable, VariableOrIntLit, VariableType}, sema::{analyzer::Analyzer as SemaAnalyzer, hir::{HirBlockStmt, HirBreakStmt, HirCompileUnit, HirContinueStmt, HirExpr, HirExprValue, HirForInit, HirForStmt, HirFuncDeclStmt, HirGlobalDeclStmt, HirIfElseBranch, HirIfStmt, HirReturnStmt, HirStatement, HirVarDeclStmt, HirWhileStmt}, symbol::{FunctionId, SymbolId, SymbolKind}}};
|
||||||
use crate::ast::types::BinaryOp as AstBinaryOp;
|
use crate::ast::types::BinaryOp as AstBinaryOp;
|
||||||
use crate::ast::types::UnaryOp as AstUnaryOp;
|
use crate::ast::types::UnaryOp as AstUnaryOp;
|
||||||
pub struct Generator<'a> {
|
pub struct Generator<'a> {
|
||||||
@@ -14,6 +14,7 @@ pub struct Generator<'a> {
|
|||||||
// if child expr isn't logical, we need to do cmp to decide which label to goto
|
// if child expr isn't logical, we need to do cmp to decide which label to goto
|
||||||
while_exit_label: Vec<(usize, usize)>, // continue exit, break exit
|
while_exit_label: Vec<(usize, usize)>, // continue exit, break exit
|
||||||
func_exit: Option<(usize, Option<Variable>)>, // (label, return_var)
|
func_exit: Option<(usize, Option<Variable>)>, // (label, return_var)
|
||||||
|
extra_global_instrs: Vec<IRInstr>,
|
||||||
label_counter: usize
|
label_counter: usize
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -25,6 +26,7 @@ impl<'a> Generator<'a> {
|
|||||||
current_exit_label: vec![],
|
current_exit_label: vec![],
|
||||||
while_exit_label: vec![],
|
while_exit_label: vec![],
|
||||||
func_exit: None,
|
func_exit: None,
|
||||||
|
extra_global_instrs: vec![],
|
||||||
label_counter: 0,
|
label_counter: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -55,7 +57,10 @@ impl<'a> Generator<'a> {
|
|||||||
instrs.extend(self.generate_var_decl(var_decl, true));
|
instrs.extend(self.generate_var_decl(var_decl, true));
|
||||||
}
|
}
|
||||||
FuncDecl(func_decl) => {
|
FuncDecl(func_decl) => {
|
||||||
instrs.extend(self.generate_func_decl(func_decl));
|
let extra_global_start = self.extra_global_instrs.len();
|
||||||
|
let func_instrs = self.generate_func_decl(func_decl);
|
||||||
|
instrs.extend(self.extra_global_instrs.drain(extra_global_start..));
|
||||||
|
instrs.extend(func_instrs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -64,17 +69,80 @@ impl<'a> Generator<'a> {
|
|||||||
|
|
||||||
fn generate_var_decl(&mut self, var_decl: HirVarDeclStmt, is_global: bool) -> Vec<IRInstr> {
|
fn generate_var_decl(&mut self, var_decl: HirVarDeclStmt, is_global: bool) -> Vec<IRInstr> {
|
||||||
let mut instrs = vec![];
|
let mut instrs = vec![];
|
||||||
let var_type = if is_global { VariableType::Global } else { VariableType::Local };
|
|
||||||
for value in var_decl.values {
|
for value in var_decl.values {
|
||||||
|
let symbol_kind = self.sema.get_symbol_kind(value.symbol);
|
||||||
|
let has_static_storage = is_global || symbol_kind == SymbolKind::StaticLocal || value.is_static_local;
|
||||||
|
let var_type = if has_static_storage { VariableType::Global } else { VariableType::Local };
|
||||||
let var = self.var_manager.declare_symbol(value.symbol, var_type, self.sema.get_symbol_type(value.symbol).into());
|
let var = self.var_manager.declare_symbol(value.symbol, var_type, self.sema.get_symbol_type(value.symbol).into());
|
||||||
|
if has_static_storage {
|
||||||
|
let init = value.value.as_ref().and_then(|expr| Self::const_init_value(expr));
|
||||||
|
let decl = IRInstr::DeclareGlobal(var, init);
|
||||||
if is_global {
|
if is_global {
|
||||||
instrs.push(IRInstr::Declare(var));
|
instrs.push(decl);
|
||||||
|
} else {
|
||||||
|
self.extra_global_instrs.push(decl);
|
||||||
|
}
|
||||||
|
} else if let Some(init) = value.value {
|
||||||
|
self.current_exit_label.push(None);
|
||||||
|
let (mut init_instrs, init_var) = match self.generate_expr(init) {
|
||||||
|
Some(res) => res,
|
||||||
|
None => {
|
||||||
|
self.current_exit_label.pop();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.current_exit_label.pop();
|
||||||
|
let target = self.var_manager.get_symbol(value.symbol).unwrap();
|
||||||
|
let init_var = self.coerce_value(&mut init_instrs, init_var.unwrap(), &target.data_type);
|
||||||
|
instrs.extend(init_instrs);
|
||||||
|
instrs.push(IRInstr::Move(target, MoveRValue::Var(init_var)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
instrs
|
instrs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn const_init_value(expr: &HirExpr) -> Option<i32> {
|
||||||
|
match &expr.value {
|
||||||
|
HirExprValue::IntLit(value) => Some(*value as i32),
|
||||||
|
HirExprValue::UnaryOp { op, operand } => {
|
||||||
|
let value = Self::const_init_value(operand)?;
|
||||||
|
match op {
|
||||||
|
AstUnaryOp::Add => Some(value),
|
||||||
|
AstUnaryOp::Sub => Some(value.wrapping_neg()),
|
||||||
|
AstUnaryOp::Not => Some((value == 0) as i32),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
HirExprValue::BinaryOp { lhs, op, rhs } => {
|
||||||
|
let lhs = Self::const_init_value(lhs)?;
|
||||||
|
match op {
|
||||||
|
AstBinaryOp::And if lhs == 0 => return Some(0),
|
||||||
|
AstBinaryOp::Or if lhs != 0 => return Some(1),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
let rhs = Self::const_init_value(rhs)?;
|
||||||
|
match op {
|
||||||
|
AstBinaryOp::Add => Some(lhs.wrapping_add(rhs)),
|
||||||
|
AstBinaryOp::Sub => Some(lhs.wrapping_sub(rhs)),
|
||||||
|
AstBinaryOp::Mul => Some(lhs.wrapping_mul(rhs)),
|
||||||
|
AstBinaryOp::Div if rhs != 0 => Some(lhs.wrapping_div(rhs)),
|
||||||
|
AstBinaryOp::Div => None,
|
||||||
|
AstBinaryOp::Mod if rhs != 0 => Some(lhs.wrapping_rem(rhs)),
|
||||||
|
AstBinaryOp::Mod => None,
|
||||||
|
AstBinaryOp::Equal => Some((lhs == rhs) as i32),
|
||||||
|
AstBinaryOp::NotEqual => Some((lhs != rhs) as i32),
|
||||||
|
AstBinaryOp::Less => Some((lhs < rhs) as i32),
|
||||||
|
AstBinaryOp::LessEqual => Some((lhs <= rhs) as i32),
|
||||||
|
AstBinaryOp::Greater => Some((lhs > rhs) as i32),
|
||||||
|
AstBinaryOp::GreaterEqual => Some((lhs >= rhs) as i32),
|
||||||
|
AstBinaryOp::And => Some((lhs != 0 && rhs != 0) as i32),
|
||||||
|
AstBinaryOp::Or => Some((lhs != 0 || rhs != 0) as i32),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn generate_func_decl(&mut self, func_decl: HirFuncDeclStmt) -> Vec<IRInstr> {
|
fn generate_func_decl(&mut self, func_decl: HirFuncDeclStmt) -> Vec<IRInstr> {
|
||||||
let parameters: Vec<Variable> = func_decl.params.iter()
|
let parameters: Vec<Variable> = func_decl.params.iter()
|
||||||
.map(|param| self.var_manager.declare_symbol(param.symbol, VariableType::Local, param.param_type.clone().into()))
|
.map(|param| self.var_manager.declare_symbol(param.symbol, VariableType::Local, param.param_type.clone().into()))
|
||||||
@@ -125,6 +193,7 @@ impl<'a> Generator<'a> {
|
|||||||
Return(return_stmt) => self.generate_return_stmt(return_stmt),
|
Return(return_stmt) => self.generate_return_stmt(return_stmt),
|
||||||
If(if_stmt) => self.generate_if_stmt(if_stmt),
|
If(if_stmt) => self.generate_if_stmt(if_stmt),
|
||||||
While(while_stmt) => self.generate_while_stmt(while_stmt),
|
While(while_stmt) => self.generate_while_stmt(while_stmt),
|
||||||
|
For(for_stmt) => self.generate_for_stmt(for_stmt),
|
||||||
Break(break_stmt) => self.generate_break_stmt(break_stmt),
|
Break(break_stmt) => self.generate_break_stmt(break_stmt),
|
||||||
Continue(continue_stmt) => self.generate_continue_stmt(continue_stmt),
|
Continue(continue_stmt) => self.generate_continue_stmt(continue_stmt),
|
||||||
Block(block_stmt) => {
|
Block(block_stmt) => {
|
||||||
@@ -153,7 +222,7 @@ impl<'a> Generator<'a> {
|
|||||||
match return_stmt.value {
|
match return_stmt.value {
|
||||||
Some(expr) => {
|
Some(expr) => {
|
||||||
self.current_exit_label.push(None);
|
self.current_exit_label.push(None);
|
||||||
let (value_instrs, value_var) = match self.generate_expr(expr) {
|
let (mut value_instrs, value_var) = match self.generate_expr(expr) {
|
||||||
Some(res) => res,
|
Some(res) => res,
|
||||||
None => {
|
None => {
|
||||||
self.current_exit_label.pop();
|
self.current_exit_label.pop();
|
||||||
@@ -161,8 +230,10 @@ impl<'a> Generator<'a> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
self.current_exit_label.pop();
|
self.current_exit_label.pop();
|
||||||
|
let target = func_exit.1.unwrap();
|
||||||
|
let value_var = self.coerce_value(&mut value_instrs, value_var.unwrap(), &target.data_type);
|
||||||
instrs.extend(value_instrs);
|
instrs.extend(value_instrs);
|
||||||
instrs.push(IRInstr::Move(func_exit.1.unwrap(), MoveRValue::Var(value_var.unwrap())));
|
instrs.push(IRInstr::Move(target, MoveRValue::Var(value_var)));
|
||||||
}
|
}
|
||||||
None => {},
|
None => {},
|
||||||
}
|
}
|
||||||
@@ -193,6 +264,69 @@ impl<'a> Generator<'a> {
|
|||||||
instrs.push(IRInstr::Label(exit_label));
|
instrs.push(IRInstr::Label(exit_label));
|
||||||
instrs
|
instrs
|
||||||
}
|
}
|
||||||
|
fn generate_for_stmt(&mut self, for_stmt: HirForStmt) -> Vec<IRInstr> {
|
||||||
|
let mut instrs = vec![];
|
||||||
|
let cond_label = self.request_label();
|
||||||
|
let body_label = self.request_label();
|
||||||
|
let update_label = self.request_label();
|
||||||
|
let exit_label = self.request_label();
|
||||||
|
|
||||||
|
if let Some(init) = for_stmt.init {
|
||||||
|
match init {
|
||||||
|
HirForInit::Expr(init) => {
|
||||||
|
self.current_exit_label.push(None);
|
||||||
|
let (init_instrs, _) = match self.generate_expr(init) {
|
||||||
|
Some(res) => res,
|
||||||
|
None => {
|
||||||
|
self.current_exit_label.pop();
|
||||||
|
return vec![];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.current_exit_label.pop();
|
||||||
|
instrs.extend(init_instrs);
|
||||||
|
}
|
||||||
|
HirForInit::VarDecl(var_decl) => {
|
||||||
|
instrs.extend(self.generate_var_decl(var_decl, false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
instrs.push(IRInstr::Label(cond_label));
|
||||||
|
if let Some(condition) = for_stmt.condition {
|
||||||
|
self.current_exit_label.push(Some((body_label, exit_label)));
|
||||||
|
let (cond_instrs, _) = match self.generate_expr(condition) {
|
||||||
|
Some(res) => res,
|
||||||
|
None => {
|
||||||
|
self.current_exit_label.pop();
|
||||||
|
return vec![];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.current_exit_label.pop();
|
||||||
|
instrs.extend(cond_instrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
instrs.push(IRInstr::Label(body_label));
|
||||||
|
self.while_exit_label.push((update_label, exit_label));
|
||||||
|
instrs.extend(self.generate_block_stmt(for_stmt.body));
|
||||||
|
self.while_exit_label.pop();
|
||||||
|
|
||||||
|
instrs.push(IRInstr::Label(update_label));
|
||||||
|
if let Some(update) = for_stmt.update {
|
||||||
|
self.current_exit_label.push(None);
|
||||||
|
let (update_instrs, _) = match self.generate_expr(update) {
|
||||||
|
Some(res) => res,
|
||||||
|
None => {
|
||||||
|
self.current_exit_label.pop();
|
||||||
|
return vec![];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.current_exit_label.pop();
|
||||||
|
instrs.extend(update_instrs);
|
||||||
|
}
|
||||||
|
instrs.push(IRInstr::Goto(cond_label));
|
||||||
|
instrs.push(IRInstr::Label(exit_label));
|
||||||
|
instrs
|
||||||
|
}
|
||||||
fn generate_if_stmt(&mut self, if_stmt: HirIfStmt) -> Vec<IRInstr> {
|
fn generate_if_stmt(&mut self, if_stmt: HirIfStmt) -> Vec<IRInstr> {
|
||||||
let mut instrs = vec![];
|
let mut instrs = vec![];
|
||||||
let then_label = self.request_label();
|
let then_label = self.request_label();
|
||||||
@@ -263,6 +397,35 @@ impl<'a> Generator<'a> {
|
|||||||
vec![]
|
vec![]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fn coerce_value(&mut self, instrs: &mut Vec<IRInstr>, var: Variable, target_type: &IRType) -> Variable {
|
||||||
|
if &var.data_type == target_type {
|
||||||
|
return var;
|
||||||
|
}
|
||||||
|
|
||||||
|
match (&var.data_type, target_type) {
|
||||||
|
(IRType::I1, IRType::I32) => {
|
||||||
|
let dest = self.var_manager.declare_unamed_local(IRType::I32);
|
||||||
|
let true_label = self.request_label();
|
||||||
|
let false_label = self.request_label();
|
||||||
|
let final_label = self.request_label();
|
||||||
|
instrs.push(IRInstr::CondGoto(var, true_label, false_label));
|
||||||
|
instrs.push(IRInstr::Label(true_label));
|
||||||
|
instrs.push(IRInstr::Move(dest.clone(), MoveRValue::ConstInt(1)));
|
||||||
|
instrs.push(IRInstr::Goto(final_label));
|
||||||
|
instrs.push(IRInstr::Label(false_label));
|
||||||
|
instrs.push(IRInstr::Move(dest.clone(), MoveRValue::ConstInt(0)));
|
||||||
|
instrs.push(IRInstr::Goto(final_label));
|
||||||
|
instrs.push(IRInstr::Label(final_label));
|
||||||
|
dest
|
||||||
|
}
|
||||||
|
(IRType::I32, IRType::I1) => {
|
||||||
|
let dest = self.var_manager.declare_temp(IRType::I1);
|
||||||
|
instrs.push(IRInstr::Cmp(dest.clone(), VariableOrIntLit::Var(var), CmpOp::Ne, VariableOrIntLit::IntLit(0)));
|
||||||
|
dest
|
||||||
|
}
|
||||||
|
_ => var,
|
||||||
|
}
|
||||||
|
}
|
||||||
fn generate_expr(&mut self, expr: HirExpr) -> Option<(Vec<IRInstr>, Option<Variable>)> {
|
fn generate_expr(&mut self, expr: HirExpr) -> Option<(Vec<IRInstr>, Option<Variable>)> {
|
||||||
// there may be some expr that doesn't produce value, like void func call
|
// there may be some expr that doesn't produce value, like void func call
|
||||||
let (mut instrs, var) = match expr.value {
|
let (mut instrs, var) = match expr.value {
|
||||||
@@ -274,9 +437,13 @@ impl<'a> Generator<'a> {
|
|||||||
HirExprValue::Var(symbol) => {
|
HirExprValue::Var(symbol) => {
|
||||||
let var = self.var_manager.get_symbol(symbol).unwrap();
|
let var = self.var_manager.get_symbol(symbol).unwrap();
|
||||||
if var.data_type.is_array() {
|
if var.data_type.is_array() {
|
||||||
|
if var.data_type.is_array_param() {
|
||||||
|
(vec![], Some(var))
|
||||||
|
} else {
|
||||||
let ptr_ty = var.data_type.decay_to_ptr().unwrap();
|
let ptr_ty = var.data_type.decay_to_ptr().unwrap();
|
||||||
let dest = self.var_manager.declare_temp(ptr_ty);
|
let dest = self.var_manager.declare_temp(ptr_ty);
|
||||||
(vec![IRInstr::Move(dest.clone(), MoveRValue::Var(var))], Some(dest))
|
(vec![IRInstr::Move(dest.clone(), MoveRValue::Var(var))], Some(dest))
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
(vec![], Some(var))
|
(vec![], Some(var))
|
||||||
}
|
}
|
||||||
@@ -286,12 +453,52 @@ impl<'a> Generator<'a> {
|
|||||||
self.current_exit_label.push(None);
|
self.current_exit_label.push(None);
|
||||||
let (mut instrs, rvalue_var) = self.generate_expr(*rvalue)?;
|
let (mut instrs, rvalue_var) = self.generate_expr(*rvalue)?;
|
||||||
self.current_exit_label.pop();
|
self.current_exit_label.pop();
|
||||||
|
let rvalue_var = self.coerce_value(&mut instrs, rvalue_var.unwrap(), &lvalue_ty);
|
||||||
let (lvalue_instrs, target, is_addr) = self.generate_lvalue(*lvalue)?;
|
let (lvalue_instrs, target, is_addr) = self.generate_lvalue(*lvalue)?;
|
||||||
instrs.extend(lvalue_instrs);
|
instrs.extend(lvalue_instrs);
|
||||||
if is_addr {
|
if is_addr {
|
||||||
instrs.push(IRInstr::Store(target.clone(), rvalue_var.unwrap()));
|
instrs.push(IRInstr::Store(target.clone(), rvalue_var));
|
||||||
} else {
|
} else {
|
||||||
instrs.push(IRInstr::Move(target.clone(), MoveRValue::Var(rvalue_var.unwrap())));
|
instrs.push(IRInstr::Move(target.clone(), MoveRValue::Var(rvalue_var)));
|
||||||
|
}
|
||||||
|
let temp_var = self.var_manager.declare_temp(lvalue_ty);
|
||||||
|
if is_addr {
|
||||||
|
instrs.push(IRInstr::Load(temp_var.clone(), target));
|
||||||
|
} else {
|
||||||
|
instrs.push(IRInstr::Move(temp_var.clone(), MoveRValue::Var(target)));
|
||||||
|
}
|
||||||
|
(instrs, Some(temp_var))
|
||||||
|
},
|
||||||
|
HirExprValue::CompoundAssign { lvalue, op, rvalue } => {
|
||||||
|
let lvalue_ty: IRType = lvalue.ty.clone().into();
|
||||||
|
let (mut instrs, target, is_addr) = self.generate_lvalue(*lvalue)?;
|
||||||
|
let mut old_var = self.var_manager.declare_temp(lvalue_ty.clone());
|
||||||
|
if is_addr {
|
||||||
|
instrs.push(IRInstr::Load(old_var.clone(), target.clone()));
|
||||||
|
} else {
|
||||||
|
instrs.push(IRInstr::Move(old_var.clone(), MoveRValue::Var(target.clone())));
|
||||||
|
}
|
||||||
|
|
||||||
|
self.current_exit_label.push(None);
|
||||||
|
let (rvalue_instrs, rvalue_var) = self.generate_expr(*rvalue)?;
|
||||||
|
self.current_exit_label.pop();
|
||||||
|
instrs.extend(rvalue_instrs);
|
||||||
|
let mut rvalue_var = rvalue_var.unwrap();
|
||||||
|
|
||||||
|
let convert_to = IRType::get_elevate_result(&old_var.data_type, &rvalue_var.data_type).unwrap();
|
||||||
|
if convert_to != old_var.data_type {
|
||||||
|
old_var = self.coerce_value(&mut instrs, old_var, &convert_to);
|
||||||
|
}
|
||||||
|
if convert_to != rvalue_var.data_type {
|
||||||
|
rvalue_var = self.coerce_value(&mut instrs, rvalue_var, &convert_to);
|
||||||
|
}
|
||||||
|
let computed = self.var_manager.declare_temp(convert_to);
|
||||||
|
instrs.push(IRInstr::Binary(computed.clone(), old_var, op.into(), rvalue_var));
|
||||||
|
let stored = self.coerce_value(&mut instrs, computed, &lvalue_ty);
|
||||||
|
if is_addr {
|
||||||
|
instrs.push(IRInstr::Store(target.clone(), stored));
|
||||||
|
} else {
|
||||||
|
instrs.push(IRInstr::Move(target.clone(), MoveRValue::Var(stored)));
|
||||||
}
|
}
|
||||||
let temp_var = self.var_manager.declare_temp(lvalue_ty);
|
let temp_var = self.var_manager.declare_temp(lvalue_ty);
|
||||||
if is_addr {
|
if is_addr {
|
||||||
@@ -342,17 +549,22 @@ impl<'a> Generator<'a> {
|
|||||||
let operand_var = operand_var.unwrap();
|
let operand_var = operand_var.unwrap();
|
||||||
let dest_var = match op {
|
let dest_var = match op {
|
||||||
AstUnaryOp::Add => {
|
AstUnaryOp::Add => {
|
||||||
let dest_var = self.var_manager.declare_temp(operand_var.data_type.clone());
|
let target_ty = if operand_var.data_type == IRType::I1 { IRType::I32 } else { operand_var.data_type.clone() };
|
||||||
|
let operand_var = self.coerce_value(&mut instrs, operand_var, &target_ty);
|
||||||
|
let dest_var = self.var_manager.declare_temp(target_ty);
|
||||||
instrs.push(IRInstr::Move(dest_var.clone(), MoveRValue::Var(operand_var)));
|
instrs.push(IRInstr::Move(dest_var.clone(), MoveRValue::Var(operand_var)));
|
||||||
dest_var
|
dest_var
|
||||||
},
|
},
|
||||||
AstUnaryOp::Sub => {
|
AstUnaryOp::Sub => {
|
||||||
let dest_var = self.var_manager.declare_temp(operand_var.data_type.clone());
|
let target_ty = if operand_var.data_type == IRType::I1 { IRType::I32 } else { operand_var.data_type.clone() };
|
||||||
|
let operand_var = self.coerce_value(&mut instrs, operand_var, &target_ty);
|
||||||
|
let dest_var = self.var_manager.declare_temp(target_ty);
|
||||||
instrs.push(IRInstr::Unary(dest_var.clone(), UnaryOp::Neg, operand_var));
|
instrs.push(IRInstr::Unary(dest_var.clone(), UnaryOp::Neg, operand_var));
|
||||||
dest_var
|
dest_var
|
||||||
},
|
},
|
||||||
AstUnaryOp::Not => {
|
AstUnaryOp::Not => {
|
||||||
let dest_var = self.var_manager.declare_unamed_local(operand_var.data_type.clone());
|
let dest_ty = if parent_is_logical { IRType::I1 } else { IRType::I32 };
|
||||||
|
let dest_var = self.var_manager.declare_unamed_local(dest_ty);
|
||||||
// child will do the cmp
|
// child will do the cmp
|
||||||
if !parent_is_logical {
|
if !parent_is_logical {
|
||||||
let exit = exit_passdown.unwrap(); // (false_exit, true_exit) (consider `not`)
|
let exit = exit_passdown.unwrap(); // (false_exit, true_exit) (consider `not`)
|
||||||
@@ -372,6 +584,35 @@ impl<'a> Generator<'a> {
|
|||||||
};
|
};
|
||||||
(instrs, Some(dest_var))
|
(instrs, Some(dest_var))
|
||||||
},
|
},
|
||||||
|
HirExprValue::IncDec { op, operand, is_prefix } => {
|
||||||
|
let result_ty: IRType = expr.ty.clone().into();
|
||||||
|
let (mut instrs, target, is_addr) = self.generate_lvalue(*operand)?;
|
||||||
|
let old_var = self.var_manager.declare_temp(result_ty.clone());
|
||||||
|
if is_addr {
|
||||||
|
instrs.push(IRInstr::Load(old_var.clone(), target.clone()));
|
||||||
|
} else {
|
||||||
|
instrs.push(IRInstr::Move(old_var.clone(), MoveRValue::Var(target.clone())));
|
||||||
|
}
|
||||||
|
|
||||||
|
let one = self.var_manager.declare_temp(result_ty.clone());
|
||||||
|
instrs.push(IRInstr::Move(one.clone(), MoveRValue::ConstInt(1)));
|
||||||
|
let new_var = self.var_manager.declare_temp(result_ty.clone());
|
||||||
|
let ir_op = match op {
|
||||||
|
crate::ast::types::IncDecOp::Inc => IRBinaryOp::Add,
|
||||||
|
crate::ast::types::IncDecOp::Dec => IRBinaryOp::Sub,
|
||||||
|
};
|
||||||
|
instrs.push(IRInstr::Binary(new_var.clone(), old_var.clone(), ir_op, one));
|
||||||
|
if is_addr {
|
||||||
|
instrs.push(IRInstr::Store(target, new_var.clone()));
|
||||||
|
} else {
|
||||||
|
instrs.push(IRInstr::Move(target, MoveRValue::Var(new_var.clone())));
|
||||||
|
}
|
||||||
|
if is_prefix {
|
||||||
|
(instrs, Some(new_var))
|
||||||
|
} else {
|
||||||
|
(instrs, Some(old_var))
|
||||||
|
}
|
||||||
|
},
|
||||||
HirExprValue::BinaryOp { lhs, op, rhs } => {
|
HirExprValue::BinaryOp { lhs, op, rhs } => {
|
||||||
// +--------+-------------------------+-------------------------+-------------------------+
|
// +--------+-------------------------+-------------------------+-------------------------+
|
||||||
// | parent | (true_exit, false_exit) | (true_exit, false_exit) | (true_exit, false_exit) |
|
// | parent | (true_exit, false_exit) | (true_exit, false_exit) | (true_exit, false_exit) |
|
||||||
@@ -430,9 +671,7 @@ impl<'a> Generator<'a> {
|
|||||||
// do implicit convert if needed
|
// do implicit convert if needed
|
||||||
// TODO: further check
|
// TODO: further check
|
||||||
if !op.is_logical() && convert_to != left_var.data_type {
|
if !op.is_logical() && convert_to != left_var.data_type {
|
||||||
let temp_var = self.var_manager.declare_temp(convert_to.clone());
|
left_var = self.coerce_value(&mut instrs, left_var, &convert_to);
|
||||||
instrs.push(IRInstr::Move(temp_var.clone(), MoveRValue::Var(left_var)));
|
|
||||||
left_var = temp_var;
|
|
||||||
}
|
}
|
||||||
let result_type;
|
let result_type;
|
||||||
match op {
|
match op {
|
||||||
@@ -444,7 +683,13 @@ impl<'a> Generator<'a> {
|
|||||||
result_type = IRType::I1;
|
result_type = IRType::I1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let dest_var = self.var_manager.declare_temp(result_type);
|
let dest_var = if op.is_logical() && parent_exit.is_none() {
|
||||||
|
self.var_manager.declare_unamed_local(IRType::I32)
|
||||||
|
} else if op.is_logical() {
|
||||||
|
self.var_manager.declare_unamed_local(result_type)
|
||||||
|
} else {
|
||||||
|
self.var_manager.declare_temp(result_type)
|
||||||
|
};
|
||||||
match op {
|
match op {
|
||||||
AstBinaryOp::And | AstBinaryOp::Or => {
|
AstBinaryOp::And | AstBinaryOp::Or => {
|
||||||
instrs.push(IRInstr::Label(exit.unwrap().1));
|
instrs.push(IRInstr::Label(exit.unwrap().1));
|
||||||
@@ -470,9 +715,7 @@ impl<'a> Generator<'a> {
|
|||||||
// true_exit:
|
// true_exit:
|
||||||
instrs.extend(right_instrs);
|
instrs.extend(right_instrs);
|
||||||
if !op.is_logical() && convert_to != right_var.data_type {
|
if !op.is_logical() && convert_to != right_var.data_type {
|
||||||
let temp_var = self.var_manager.declare_temp(convert_to);
|
right_var = self.coerce_value(&mut instrs, right_var, &convert_to);
|
||||||
instrs.push(IRInstr::Move(temp_var.clone(), MoveRValue::Var(right_var)));
|
|
||||||
right_var = temp_var;
|
|
||||||
}
|
}
|
||||||
if !op.is_logical() {
|
if !op.is_logical() {
|
||||||
if let Some((true_exit, false_exit)) = parent_exit {
|
if let Some((true_exit, false_exit)) = parent_exit {
|
||||||
@@ -488,6 +731,17 @@ impl<'a> Generator<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if op.is_logical() {
|
if op.is_logical() {
|
||||||
|
if parent_exit.is_none() {
|
||||||
|
let (true_exit, _, false_exit) = exit.unwrap();
|
||||||
|
let final_exit = self.request_label();
|
||||||
|
instrs.push(IRInstr::Label(true_exit));
|
||||||
|
instrs.push(IRInstr::Move(dest_var.clone(), MoveRValue::ConstInt(1)));
|
||||||
|
instrs.push(IRInstr::Goto(final_exit));
|
||||||
|
instrs.push(IRInstr::Label(false_exit));
|
||||||
|
instrs.push(IRInstr::Move(dest_var.clone(), MoveRValue::ConstInt(0)));
|
||||||
|
instrs.push(IRInstr::Goto(final_exit));
|
||||||
|
instrs.push(IRInstr::Label(final_exit));
|
||||||
|
}
|
||||||
return Some((instrs, Some(dest_var)));
|
return Some((instrs, Some(dest_var)));
|
||||||
} else {
|
} else {
|
||||||
if op.is_cmp() {
|
if op.is_cmp() {
|
||||||
@@ -517,12 +771,13 @@ impl<'a> Generator<'a> {
|
|||||||
let mut arg_vars = vec![];
|
let mut arg_vars = vec![];
|
||||||
let func_def = self.ir_function(func_id);
|
let func_def = self.ir_function(func_id);
|
||||||
|
|
||||||
for arg in args.into_iter() {
|
for (arg, param_type) in args.into_iter().zip(func_def.parameter_types.iter()) {
|
||||||
self.current_exit_label.push(None);
|
self.current_exit_label.push(None);
|
||||||
let (arg_instrs, arg_var) = self.generate_expr(arg)?;
|
let (mut arg_instrs, arg_var) = self.generate_expr(arg)?;
|
||||||
self.current_exit_label.pop();
|
self.current_exit_label.pop();
|
||||||
|
let arg_var = self.coerce_value(&mut arg_instrs, arg_var.unwrap(), param_type);
|
||||||
instrs.extend(arg_instrs);
|
instrs.extend(arg_instrs);
|
||||||
arg_vars.push(arg_var.unwrap());
|
arg_vars.push(arg_var);
|
||||||
}
|
}
|
||||||
let ret_variable = if matches!(func_def.return_type, IRType::Void) {
|
let ret_variable = if matches!(func_def.return_type, IRType::Void) {
|
||||||
None
|
None
|
||||||
@@ -534,10 +789,15 @@ impl<'a> Generator<'a> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
if let Some((true_exit, false_exit)) = self.current_exit_label.last().cloned().flatten() {
|
if let Some((true_exit, false_exit)) = self.current_exit_label.last().cloned().flatten() {
|
||||||
|
let var = var.clone().unwrap();
|
||||||
|
if matches!(var.data_type, IRType::Array(_, _) | IRType::Ptr(_)) {
|
||||||
|
instrs.push(IRInstr::Goto(true_exit));
|
||||||
|
} else {
|
||||||
let cmp_var = self.var_manager.declare_temp(IRType::I1);
|
let cmp_var = self.var_manager.declare_temp(IRType::I1);
|
||||||
instrs.push(IRInstr::Cmp(cmp_var.clone(), VariableOrIntLit::Var(var.clone().unwrap()), CmpOp::Ne, VariableOrIntLit::IntLit(0)));
|
instrs.push(IRInstr::Cmp(cmp_var.clone(), VariableOrIntLit::Var(var.clone()), CmpOp::Ne, VariableOrIntLit::IntLit(0)));
|
||||||
instrs.push(IRInstr::CondGoto(cmp_var, true_exit, false_exit));
|
instrs.push(IRInstr::CondGoto(cmp_var, true_exit, false_exit));
|
||||||
Some((instrs, var))
|
}
|
||||||
|
Some((instrs, Some(var)))
|
||||||
} else {
|
} else {
|
||||||
Some((instrs, var))
|
Some((instrs, var))
|
||||||
}
|
}
|
||||||
@@ -564,8 +824,12 @@ impl<'a> Generator<'a> {
|
|||||||
HirExprValue::Var(symbol) => {
|
HirExprValue::Var(symbol) => {
|
||||||
let var = self.var_manager.get_symbol(symbol).unwrap();
|
let var = self.var_manager.get_symbol(symbol).unwrap();
|
||||||
if var.data_type.is_array() {
|
if var.data_type.is_array() {
|
||||||
|
if var.data_type.is_array_param() {
|
||||||
|
(vec![], var)
|
||||||
|
} else {
|
||||||
let ptr = self.var_manager.declare_temp(var.data_type.decay_to_ptr().unwrap());
|
let ptr = self.var_manager.declare_temp(var.data_type.decay_to_ptr().unwrap());
|
||||||
(vec![IRInstr::Move(ptr.clone(), MoveRValue::Var(var))], ptr)
|
(vec![IRInstr::Move(ptr.clone(), MoveRValue::Var(var))], ptr)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
(vec![], var)
|
(vec![], var)
|
||||||
}
|
}
|
||||||
@@ -577,12 +841,13 @@ impl<'a> Generator<'a> {
|
|||||||
let (index_instrs, index_var) = self.generate_expr(index)?;
|
let (index_instrs, index_var) = self.generate_expr(index)?;
|
||||||
self.current_exit_label.pop();
|
self.current_exit_label.pop();
|
||||||
instrs.extend(index_instrs);
|
instrs.extend(index_instrs);
|
||||||
|
let index_var = self.coerce_value(&mut instrs, index_var.unwrap(), &IRType::I32);
|
||||||
let addr_ty = IRType::Ptr(Box::new(elem_ty.into()));
|
let addr_ty = IRType::Ptr(Box::new(elem_ty.into()));
|
||||||
let dest = self.var_manager.declare_temp(addr_ty);
|
let dest = self.var_manager.declare_temp(addr_ty);
|
||||||
let elem_size_var = self.var_manager.declare_temp(IRType::I32);
|
let elem_size_var = self.var_manager.declare_temp(IRType::I32);
|
||||||
let offset = self.var_manager.declare_temp(IRType::I32);
|
let offset = self.var_manager.declare_temp(IRType::I32);
|
||||||
instrs.push(IRInstr::Move(elem_size_var.clone(), MoveRValue::ConstInt(elem_size as i32)));
|
instrs.push(IRInstr::Move(elem_size_var.clone(), MoveRValue::ConstInt(elem_size as i32)));
|
||||||
instrs.push(IRInstr::Binary(offset.clone(), index_var.unwrap(), AstBinaryOp::Mul.into(), elem_size_var));
|
instrs.push(IRInstr::Binary(offset.clone(), index_var, AstBinaryOp::Mul.into(), elem_size_var));
|
||||||
instrs.push(IRInstr::Binary(dest.clone(), base, AstBinaryOp::Add.into(), offset));
|
instrs.push(IRInstr::Binary(dest.clone(), base, AstBinaryOp::Add.into(), offset));
|
||||||
Some((instrs, dest))
|
Some((instrs, dest))
|
||||||
}
|
}
|
||||||
@@ -663,8 +928,8 @@ mod tests {
|
|||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use crate::ast::graph::AstGraphExt;
|
use crate::ast::graph::AstGraphExt;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use crate::frontend::lexer::Lexer;
|
use crate::ast::parser::Parser;
|
||||||
use crate::frontend::parser::Parser;
|
use crate::lexer::lexer::Lexer;
|
||||||
use crate::sema::analyzer::Analyzer;
|
use crate::sema::analyzer::Analyzer;
|
||||||
use crate::utils::case_list::CaseList;
|
use crate::utils::case_list::CaseList;
|
||||||
use crate::utils::num_sequence::NumberSequence;
|
use crate::utils::num_sequence::NumberSequence;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
pub mod generator;
|
pub mod generator;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
pub mod err;
|
pub mod err;
|
||||||
|
pub mod cfg;
|
||||||
|
|||||||
+29
-3
@@ -4,6 +4,7 @@ use crate::ast::types::Type as AstType;
|
|||||||
use crate::ast::types::BinaryOp as AstBinaryOp;
|
use crate::ast::types::BinaryOp as AstBinaryOp;
|
||||||
use crate::ir::err::IRError;
|
use crate::ir::err::IRError;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub enum VariableOrIntLit {
|
pub enum VariableOrIntLit {
|
||||||
Var(Variable),
|
Var(Variable),
|
||||||
IntLit(i32),
|
IntLit(i32),
|
||||||
@@ -17,8 +18,10 @@ impl Display for VariableOrIntLit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[derive(Clone)]
|
||||||
pub enum IRInstr {
|
pub enum IRInstr {
|
||||||
Declare(Variable),
|
Declare(Variable),
|
||||||
|
DeclareGlobal(Variable, Option<i32>),
|
||||||
DefineFunc(Function, Vec<Variable>, Vec<IRInstr>),
|
DefineFunc(Function, Vec<Variable>, Vec<IRInstr>),
|
||||||
Entry,
|
Entry,
|
||||||
Binary(Variable, Variable, BinaryOp, Variable),
|
Binary(Variable, Variable, BinaryOp, Variable),
|
||||||
@@ -52,6 +55,8 @@ impl Display for IRInstr {
|
|||||||
IRInstr::Load(dest, addr) => write!(f, "{} = *{}", dest, addr),
|
IRInstr::Load(dest, addr) => write!(f, "{} = *{}", dest, addr),
|
||||||
IRInstr::Store(addr, value) => write!(f, "*{} = {}", addr, value),
|
IRInstr::Store(addr, value) => write!(f, "*{} = {}", addr, value),
|
||||||
IRInstr::Declare(var) => write!(f, "declare {}", var.to_decl_string()),
|
IRInstr::Declare(var) => write!(f, "declare {}", var.to_decl_string()),
|
||||||
|
IRInstr::DeclareGlobal(var, Some(init)) => write!(f, "declare {} = {}", var.to_decl_string(), init),
|
||||||
|
IRInstr::DeclareGlobal(var, None) => write!(f, "declare {}", var.to_decl_string()),
|
||||||
IRInstr::DefineFunc(func, args, body) => {
|
IRInstr::DefineFunc(func, args, body) => {
|
||||||
let body_str = body.iter().map(|instr| format!(" {}", instr)).collect::<Vec<_>>().join("\n");
|
let body_str = body.iter().map(|instr| format!(" {}", instr)).collect::<Vec<_>>().join("\n");
|
||||||
write!(f, "define {} {{\n{}\n}}", func.to_decl_string(args), body_str)
|
write!(f, "define {} {{\n{}\n}}", func.to_decl_string(args), body_str)
|
||||||
@@ -86,6 +91,10 @@ impl IRType {
|
|||||||
matches!(self, IRType::Array(_, _))
|
matches!(self, IRType::Array(_, _))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_array_param(&self) -> bool {
|
||||||
|
matches!(self, IRType::Array(_, dims) if dims.first().is_some_and(|dim| *dim == 0))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn decay_to_ptr(&self) -> Option<IRType> {
|
pub fn decay_to_ptr(&self) -> Option<IRType> {
|
||||||
match self {
|
match self {
|
||||||
IRType::Array(elem, dims) => {
|
IRType::Array(elem, dims) => {
|
||||||
@@ -115,10 +124,11 @@ impl IRType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_elevate_result(lhs: &IRType, rhs: &IRType) -> Option<IRType> {
|
pub fn get_elevate_result(lhs: &IRType, rhs: &IRType) -> Option<IRType> {
|
||||||
|
if matches!((lhs, rhs), (IRType::I32 | IRType::I1, IRType::I32 | IRType::I1)) {
|
||||||
|
return Some(IRType::I32);
|
||||||
|
}
|
||||||
if lhs == rhs {
|
if lhs == rhs {
|
||||||
Some(lhs.clone())
|
Some(lhs.clone())
|
||||||
} else if (*lhs == IRType::I32 && *rhs == IRType::I1) || (*lhs == IRType::I1 && *rhs == IRType::I32) {
|
|
||||||
Some(IRType::I32)
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@@ -165,6 +175,7 @@ impl From<AstType> for IRType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[derive(Clone)]
|
||||||
pub enum MoveRValue {
|
pub enum MoveRValue {
|
||||||
Var(Variable),
|
Var(Variable),
|
||||||
ConstInt(i32),
|
ConstInt(i32),
|
||||||
@@ -236,6 +247,18 @@ impl Variable {
|
|||||||
_ => format!("{} {}", self.data_type, self),
|
_ => format!("{} {}", self.data_type, self),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn to_typed_string_with_type(&self, ty: &IRType) -> String {
|
||||||
|
match ty {
|
||||||
|
IRType::Array(_, _) => {
|
||||||
|
let (base_type, dims) = ty.base_type_and_dims();
|
||||||
|
let dims_str = dims.iter().map(|dim| format!("[{}]", dim)).collect::<Vec<_>>().join("");
|
||||||
|
format!("{} {}{}", base_type, self, dims_str)
|
||||||
|
}
|
||||||
|
IRType::Ptr(_) => format!("{}* {}", ty.scalar_base_type(), self),
|
||||||
|
_ => format!("{} {}", ty, self),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Function {
|
pub struct Function {
|
||||||
@@ -247,7 +270,7 @@ pub struct Function {
|
|||||||
impl Function {
|
impl Function {
|
||||||
pub fn to_call_string(&self, args: &Vec<Variable>) -> String {
|
pub fn to_call_string(&self, args: &Vec<Variable>) -> String {
|
||||||
assert!(args.len() == self.parameter_types.len());
|
assert!(args.len() == self.parameter_types.len());
|
||||||
let args_str = args.iter().zip(self.parameter_types.iter()).map(|(arg, param)| format!("{} {}", param, arg)).collect::<Vec<_>>().join(", ");
|
let args_str = args.iter().zip(self.parameter_types.iter()).map(|(arg, param)| arg.to_typed_string_with_type(param)).collect::<Vec<_>>().join(", ");
|
||||||
format!("{} @{}({})", self.return_type, self.name, args_str)
|
format!("{} @{}({})", self.return_type, self.name, args_str)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -260,6 +283,7 @@ impl Function {
|
|||||||
format!("{} @{}({})", self.return_type, self.name, params_str)
|
format!("{} @{}({})", self.return_type, self.name, params_str)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[derive(Clone)]
|
||||||
pub enum BinaryOp {
|
pub enum BinaryOp {
|
||||||
Add,
|
Add,
|
||||||
Sub,
|
Sub,
|
||||||
@@ -267,6 +291,7 @@ pub enum BinaryOp {
|
|||||||
Div,
|
Div,
|
||||||
Mod,
|
Mod,
|
||||||
}
|
}
|
||||||
|
#[derive(Clone)]
|
||||||
pub enum CmpOp {
|
pub enum CmpOp {
|
||||||
Le,
|
Le,
|
||||||
Lt,
|
Lt,
|
||||||
@@ -275,6 +300,7 @@ pub enum CmpOp {
|
|||||||
Ne,
|
Ne,
|
||||||
Eq,
|
Eq,
|
||||||
}
|
}
|
||||||
|
#[derive(Clone)]
|
||||||
pub enum UnaryOp {
|
pub enum UnaryOp {
|
||||||
Neg
|
Neg
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
use crate::ast::err::ParseError;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Error)]
|
||||||
|
pub enum LexingError {
|
||||||
|
#[error("invalid int literal")]
|
||||||
|
InvalidIntLiteral,
|
||||||
|
#[error("invalid ident")]
|
||||||
|
InvalidIdent,
|
||||||
|
#[error("comment unterminated")]
|
||||||
|
UnterminatedComment,
|
||||||
|
#[error("unrecognized token: {0}")]
|
||||||
|
UnrecognizedToken(String),
|
||||||
|
}
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Error)]
|
||||||
|
pub enum FrontendError {
|
||||||
|
#[error(transparent)]
|
||||||
|
Lexing(#[from] LexingError),
|
||||||
|
#[error(transparent)]
|
||||||
|
Parse(#[from] ParseError),
|
||||||
|
}
|
||||||
@@ -1,9 +1,8 @@
|
|||||||
use std::{io::BufRead, str::FromStr};
|
use std::{str::FromStr, sync::LazyLock};
|
||||||
|
|
||||||
use codespan_reporting::diagnostic;
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::{diagnostic::{Diagnositics, span::{self, Span}}, frontend::{err::LexingError, types::{TokenValue, TypeIdent}}};
|
use crate::{diagnostic::{Diagnositics, span::Span}, lexer::{err::LexingError, types::{TokenValue, TypeIdent}}};
|
||||||
|
|
||||||
use super::types::Token;
|
use super::types::Token;
|
||||||
|
|
||||||
@@ -17,7 +16,7 @@ pub struct Lexer {
|
|||||||
|
|
||||||
const WHITESPACE_CHARS: &[char] = &[' ', '\t', '\n', '\r'];
|
const WHITESPACE_CHARS: &[char] = &[' ', '\t', '\n', '\r'];
|
||||||
const DELIMITER_CHARS: &[char] = &[
|
const DELIMITER_CHARS: &[char] = &[
|
||||||
'+', '-', '*', '/', '%', '=', '!', '<', '>', '(', ')', '[', ']', ',', ';', '{', '|', '&'
|
'+', '-', '*', '/', '%', '=', '!', '<', '>', '(', ')', '[', ']', ',', ';', '{', '}', '|', '&'
|
||||||
];
|
];
|
||||||
struct Cursor {
|
struct Cursor {
|
||||||
chars: Vec<char>,
|
chars: Vec<char>,
|
||||||
@@ -270,11 +269,55 @@ fn parse_puncuation(
|
|||||||
};
|
};
|
||||||
let c = str_iter.peek().ok_or(LexParseError::NotMatched)?;
|
let c = str_iter.peek().ok_or(LexParseError::NotMatched)?;
|
||||||
let token_value = match c {
|
let token_value = match c {
|
||||||
'+' => TokenValue::Plus,
|
'+' => {
|
||||||
'-' => TokenValue::Minus,
|
str_iter.advance(1);
|
||||||
'*' => TokenValue::Star,
|
if let Some('+') = str_iter.peek() {
|
||||||
'/' => TokenValue::Slash,
|
TokenValue::PlusPlus
|
||||||
'%' => TokenValue::Percent,
|
} else if let Some('=') = str_iter.peek() {
|
||||||
|
TokenValue::PlusEqual
|
||||||
|
} else {
|
||||||
|
str_iter.back(1);
|
||||||
|
TokenValue::Plus
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'-' => {
|
||||||
|
str_iter.advance(1);
|
||||||
|
if let Some('-') = str_iter.peek() {
|
||||||
|
TokenValue::MinusMinus
|
||||||
|
} else if let Some('=') = str_iter.peek() {
|
||||||
|
TokenValue::MinusEqual
|
||||||
|
} else {
|
||||||
|
str_iter.back(1);
|
||||||
|
TokenValue::Minus
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'*' => {
|
||||||
|
str_iter.advance(1);
|
||||||
|
if let Some('=') = str_iter.peek() {
|
||||||
|
TokenValue::StarEqual
|
||||||
|
} else {
|
||||||
|
str_iter.back(1);
|
||||||
|
TokenValue::Star
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'/' => {
|
||||||
|
str_iter.advance(1);
|
||||||
|
if let Some('=') = str_iter.peek() {
|
||||||
|
TokenValue::SlashEqual
|
||||||
|
} else {
|
||||||
|
str_iter.back(1);
|
||||||
|
TokenValue::Slash
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'%' => {
|
||||||
|
str_iter.advance(1);
|
||||||
|
if let Some('=') = str_iter.peek() {
|
||||||
|
TokenValue::PercentEqual
|
||||||
|
} else {
|
||||||
|
str_iter.back(1);
|
||||||
|
TokenValue::Percent
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
'=' => get_value_by_next_char(str_iter, TokenValue::Equal, TokenValue::DoubleEqual),
|
'=' => get_value_by_next_char(str_iter, TokenValue::Equal, TokenValue::DoubleEqual),
|
||||||
'!' => {
|
'!' => {
|
||||||
@@ -315,6 +358,17 @@ fn parse_puncuation(
|
|||||||
Ok(token_value)
|
Ok(token_value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// static KEYWORDS: LazyLock<HashMap<&'static str, TokenValue>> = LazyLock::new(|| {
|
||||||
|
// let mut m = HashMap::new();
|
||||||
|
// m.insert("if", TokenValue::If);
|
||||||
|
// m.insert("else", TokenValue::Else);
|
||||||
|
// m.insert("while", TokenValue::While);
|
||||||
|
// m.insert("for", TokenValue::For);
|
||||||
|
// m.insert("return", TokenValue::Return);
|
||||||
|
// m.insert("break", TokenValue::Break);
|
||||||
|
// m.insert("continue", TokenValue::Continue);
|
||||||
|
// m
|
||||||
|
// });
|
||||||
fn parse_ident(
|
fn parse_ident(
|
||||||
str_iter: &mut Cursor,
|
str_iter: &mut Cursor,
|
||||||
) -> Result<TokenValue, LexParseError> {
|
) -> Result<TokenValue, LexParseError> {
|
||||||
@@ -346,12 +400,18 @@ fn parse_ident(
|
|||||||
if name.eq("while") {
|
if name.eq("while") {
|
||||||
return Ok(TokenValue::While);
|
return Ok(TokenValue::While);
|
||||||
}
|
}
|
||||||
|
if name.eq("for") {
|
||||||
|
return Ok(TokenValue::For);
|
||||||
|
}
|
||||||
if name.eq("break") {
|
if name.eq("break") {
|
||||||
return Ok(TokenValue::Break);
|
return Ok(TokenValue::Break);
|
||||||
}
|
}
|
||||||
if name.eq("continue") {
|
if name.eq("continue") {
|
||||||
return Ok(TokenValue::Continue);
|
return Ok(TokenValue::Continue);
|
||||||
}
|
}
|
||||||
|
if name.eq("static") {
|
||||||
|
return Ok(TokenValue::Static);
|
||||||
|
}
|
||||||
if let Some(type_ident) = TypeIdent::from_str(&name).ok() {
|
if let Some(type_ident) = TypeIdent::from_str(&name).ok() {
|
||||||
return Ok(TokenValue::TypeIdent(type_ident));
|
return Ok(TokenValue::TypeIdent(type_ident));
|
||||||
}
|
}
|
||||||
@@ -359,6 +419,7 @@ fn parse_ident(
|
|||||||
}
|
}
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use std::io::BufRead;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use crate::utils::case_list::CaseList;
|
use crate::utils::case_list::CaseList;
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
pub mod types;
|
|
||||||
pub mod lexer;
|
|
||||||
pub mod parser;
|
|
||||||
pub mod err;
|
pub mod err;
|
||||||
|
pub mod lexer;
|
||||||
|
pub mod types;
|
||||||
@@ -17,6 +17,8 @@ pub enum TokenValue {
|
|||||||
TypeIdent(TypeIdent),
|
TypeIdent(TypeIdent),
|
||||||
|
|
||||||
Plus, Minus, Star, Slash, Percent,
|
Plus, Minus, Star, Slash, Percent,
|
||||||
|
PlusPlus, MinusMinus,
|
||||||
|
PlusEqual, MinusEqual, StarEqual, SlashEqual, PercentEqual,
|
||||||
Equal, DoubleEqual, Not, NotEqual, Less, LessEqual, Greater, GreaterEqual, And, Or,
|
Equal, DoubleEqual, Not, NotEqual, Less, LessEqual, Greater, GreaterEqual, And, Or,
|
||||||
|
|
||||||
LParen, RParen,
|
LParen, RParen,
|
||||||
@@ -24,9 +26,9 @@ pub enum TokenValue {
|
|||||||
LBracket, RBracket,
|
LBracket, RBracket,
|
||||||
Comma, Semicolon,
|
Comma, Semicolon,
|
||||||
|
|
||||||
If, Else, While, Return, Break, Continue,
|
If, Else, While, For, Return, Break, Continue, Static,
|
||||||
|
|
||||||
// Eof,
|
Eof,
|
||||||
Unrecognized,
|
Unrecognized,
|
||||||
}
|
}
|
||||||
impl TokenValue {
|
impl TokenValue {
|
||||||
@@ -53,6 +55,13 @@ impl std::fmt::Display for TokenValue {
|
|||||||
TokenValue::TypeIdent(t) => write!(f, "type {}", t.as_ref()),
|
TokenValue::TypeIdent(t) => write!(f, "type {}", t.as_ref()),
|
||||||
TokenValue::Plus => write!(f, "`+`"),
|
TokenValue::Plus => write!(f, "`+`"),
|
||||||
TokenValue::Minus => write!(f, "`-`"),
|
TokenValue::Minus => write!(f, "`-`"),
|
||||||
|
TokenValue::PlusPlus => write!(f, "`++`"),
|
||||||
|
TokenValue::MinusMinus => write!(f, "`--`"),
|
||||||
|
TokenValue::PlusEqual => write!(f, "`+=`"),
|
||||||
|
TokenValue::MinusEqual => write!(f, "`-=`"),
|
||||||
|
TokenValue::StarEqual => write!(f, "`*=`"),
|
||||||
|
TokenValue::SlashEqual => write!(f, "`/=`"),
|
||||||
|
TokenValue::PercentEqual => write!(f, "`%=`"),
|
||||||
TokenValue::Star => write!(f, "`*`"),
|
TokenValue::Star => write!(f, "`*`"),
|
||||||
TokenValue::Slash => write!(f, "`/`"),
|
TokenValue::Slash => write!(f, "`/`"),
|
||||||
TokenValue::Percent => write!(f, "`%`"),
|
TokenValue::Percent => write!(f, "`%`"),
|
||||||
@@ -77,10 +86,12 @@ impl std::fmt::Display for TokenValue {
|
|||||||
TokenValue::If => write!(f, "if"),
|
TokenValue::If => write!(f, "if"),
|
||||||
TokenValue::Else => write!(f, "else"),
|
TokenValue::Else => write!(f, "else"),
|
||||||
TokenValue::While => write!(f, "while"),
|
TokenValue::While => write!(f, "while"),
|
||||||
|
TokenValue::For => write!(f, "for"),
|
||||||
TokenValue::Return => write!(f, "return"),
|
TokenValue::Return => write!(f, "return"),
|
||||||
TokenValue::Break => write!(f, "break"),
|
TokenValue::Break => write!(f, "break"),
|
||||||
TokenValue::Continue => write!(f, "continue"),
|
TokenValue::Continue => write!(f, "continue"),
|
||||||
// TokenValue::Eof => write!(f, "<EOF>"),
|
TokenValue::Static => write!(f, "static"),
|
||||||
|
TokenValue::Eof => write!(f, "end of input"),
|
||||||
TokenValue::Unrecognized => write!(f, "unrecognized"),
|
TokenValue::Unrecognized => write!(f, "unrecognized"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -93,6 +104,7 @@ pub enum TokenKind {
|
|||||||
TypeIdent,
|
TypeIdent,
|
||||||
|
|
||||||
Plus, Minus, Star, Slash, Percent,
|
Plus, Minus, Star, Slash, Percent,
|
||||||
|
PlusEqual, MinusEqual, StarEqual, SlashEqual, PercentEqual,
|
||||||
Equal, DoubleEqual, NotEqual, Less, LessEqual, Greater, GreaterEqual,
|
Equal, DoubleEqual, NotEqual, Less, LessEqual, Greater, GreaterEqual,
|
||||||
|
|
||||||
LParen, RParen,
|
LParen, RParen,
|
||||||
@@ -100,9 +112,9 @@ pub enum TokenKind {
|
|||||||
LBracket, RBracket,
|
LBracket, RBracket,
|
||||||
Comma, Semicolon,
|
Comma, Semicolon,
|
||||||
|
|
||||||
If, Else, While, Return, Break, Continue,
|
If, Else, While, For, Return, Break, Continue, Static,
|
||||||
|
|
||||||
// Eof,
|
Eof,
|
||||||
Unrecognized,
|
Unrecognized,
|
||||||
}
|
}
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, EnumString, AsRefStr)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, EnumString, AsRefStr)]
|
||||||
+16
-3
@@ -1,4 +1,4 @@
|
|||||||
mod frontend;
|
mod lexer;
|
||||||
mod ast;
|
mod ast;
|
||||||
mod ir;
|
mod ir;
|
||||||
mod backend;
|
mod backend;
|
||||||
@@ -11,8 +11,9 @@ use std::{fs::File, io::BufRead};
|
|||||||
|
|
||||||
use clap::Parser as ArgParser;
|
use clap::Parser as ArgParser;
|
||||||
|
|
||||||
use crate::{frontend::{lexer::Lexer, parser::Parser}, ir::generator::Generator, sema::analyzer::Analyzer};
|
use crate::{ast::parser::Parser, ir::{cfg, generator::Generator}, lexer::lexer::Lexer, sema::analyzer::Analyzer};
|
||||||
use crate::backend::generator::Generator as ASMGerenerator;
|
use crate::backend::generator::Generator as ASMGerenerator;
|
||||||
|
|
||||||
/// Simple minic compiler built by Rust
|
/// Simple minic compiler built by Rust
|
||||||
#[derive(ArgParser, Debug)]
|
#[derive(ArgParser, Debug)]
|
||||||
#[command(version, about, long_about = None)]
|
#[command(version, about, long_about = None)]
|
||||||
@@ -33,6 +34,9 @@ struct Args {
|
|||||||
/// Output file for the generated code (default: stdout)
|
/// Output file for the generated code (default: stdout)
|
||||||
#[arg(short = 'o', long = "output")]
|
#[arg(short = 'o', long = "output")]
|
||||||
output: Option<String>,
|
output: Option<String>,
|
||||||
|
/// Output CFG graph in Graphviz dot format
|
||||||
|
#[arg(long = "cfg-dot")]
|
||||||
|
cfg_dot: Option<String>,
|
||||||
/// Source file to compile
|
/// Source file to compile
|
||||||
source: String,
|
source: String,
|
||||||
}
|
}
|
||||||
@@ -73,19 +77,28 @@ fn main() {
|
|||||||
let (tokens, diagnostics) = lexer.finish();
|
let (tokens, diagnostics) = lexer.finish();
|
||||||
if !diagnostics.is_empty() {
|
if !diagnostics.is_empty() {
|
||||||
diagnostics.print(&format!("{}", source_path.display()), &full_text);
|
diagnostics.print(&format!("{}", source_path.display()), &full_text);
|
||||||
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
let mut parser = Parser::new(tokens, diagnostics);
|
let mut parser = Parser::new(tokens, diagnostics);
|
||||||
let compile_unit = parser.parse();
|
let compile_unit = parser.parse();
|
||||||
if !parser.diagnostics.is_empty() {
|
if !parser.diagnostics.is_empty() {
|
||||||
parser.diagnostics.print(&format!("{}", source_path.display()), &full_text);
|
parser.diagnostics.print(&format!("{}", source_path.display()), &full_text);
|
||||||
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
let mut analyzer = Analyzer::new();
|
let mut analyzer = Analyzer::new();
|
||||||
let hir = analyzer.analyze(compile_unit);
|
let hir = analyzer.analyze(compile_unit);
|
||||||
if !analyzer.get_diagnostics().is_empty() {
|
if !analyzer.get_diagnostics().is_empty() {
|
||||||
analyzer.get_diagnostics().print(&format!("{}", source_path.display()), &full_text);
|
analyzer.get_diagnostics().print(&format!("{}", source_path.display()), &full_text);
|
||||||
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
let mut generator = Generator::new(&analyzer);
|
let mut generator = Generator::new(&analyzer);
|
||||||
let ir = generator.emit(hir);
|
let ir = cfg::optimize_ir(generator.emit(hir));
|
||||||
|
if let Some(cfg_dot_path) = &args.cfg_dot {
|
||||||
|
match std::fs::write(cfg_dot_path, cfg::ir_to_cfg_dot(&ir)) {
|
||||||
|
Ok(_) => println!("CFG graph written to {}", cfg_dot_path),
|
||||||
|
Err(e) => eprintln!("Failed to write CFG graph to {}: {}", cfg_dot_path, e),
|
||||||
|
}
|
||||||
|
}
|
||||||
if args.output_ir {
|
if args.output_ir {
|
||||||
if let Some(output_path) = args.output {
|
if let Some(output_path) = args.output {
|
||||||
match std::fs::write(&output_path, ir.iter().map(|instr| instr.to_string()).collect::<Vec<_>>().join("\n")) {
|
match std::fs::write(&output_path, ir.iter().map(|instr| instr.to_string()).collect::<Vec<_>>().join("\n")) {
|
||||||
|
|||||||
+213
-27
@@ -1,16 +1,16 @@
|
|||||||
use std::collections::BTreeMap;
|
use std::collections::{BTreeMap, BTreeSet};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::types::{
|
ast::types::{
|
||||||
ArrayDimension, BinaryOp, BlockStmt, BreakStmt, CompileUnit, ContinueStmt, Expr, ExprValue, FuncDeclStmt,
|
ArrayDimension, BinaryOp, BlockStmt, BreakStmt, CompileUnit, ContinueStmt, Expr, ExprValue, ForInit, ForStmt, FuncDeclStmt,
|
||||||
GlobalDeclStmt, IfElseBranch, IfStmt, ReturnStmt, Statement, VarDeclStmt, WhileStmt,
|
GlobalDeclStmt, IfElseBranch, IfStmt, ReturnStmt, Statement, StorageClass, VarDeclStmt, WhileStmt,
|
||||||
},
|
},
|
||||||
diagnostic::{span::Span, Diagnositics},
|
diagnostic::{span::Span, Diagnositics},
|
||||||
sema::{
|
sema::{
|
||||||
err::SemaError,
|
err::SemaError,
|
||||||
hir::{
|
hir::{
|
||||||
HirBlockStmt, HirBreakStmt, HirCompileUnit, HirContinueStmt, HirExpr, HirExprValue,
|
HirBlockStmt, HirBreakStmt, HirCompileUnit, HirContinueStmt, HirExpr, HirExprValue,
|
||||||
HirFuncDeclStmt, HirGlobalDeclStmt, HirIfElseBranch, HirIfStmt, HirParam,
|
HirForInit, HirForStmt, HirFuncDeclStmt, HirGlobalDeclStmt, HirIfElseBranch, HirIfStmt, HirParam,
|
||||||
HirReturnStmt, HirStatement, HirVarDeclStmt, HirVarDeclStmtValue, HirWhileStmt,
|
HirReturnStmt, HirStatement, HirVarDeclStmt, HirVarDeclStmtValue, HirWhileStmt,
|
||||||
},
|
},
|
||||||
symbol::{FunctionId, FunctionSig, SymbolId, SymbolKind, SymbolTable},
|
symbol::{FunctionId, FunctionSig, SymbolId, SymbolKind, SymbolTable},
|
||||||
@@ -21,6 +21,7 @@ use crate::{
|
|||||||
pub struct Analyzer {
|
pub struct Analyzer {
|
||||||
symbols: SymbolTable,
|
symbols: SymbolTable,
|
||||||
function_map: BTreeMap<String, FunctionId>,
|
function_map: BTreeMap<String, FunctionId>,
|
||||||
|
builtin_functions: BTreeSet<FunctionId>,
|
||||||
functions: Vec<FunctionSig>,
|
functions: Vec<FunctionSig>,
|
||||||
current_func_return_type: Option<SemaType>,
|
current_func_return_type: Option<SemaType>,
|
||||||
diagnostic: Diagnositics,
|
diagnostic: Diagnositics,
|
||||||
@@ -32,6 +33,7 @@ impl Analyzer {
|
|||||||
let mut analyzer = Self {
|
let mut analyzer = Self {
|
||||||
symbols: SymbolTable::new(),
|
symbols: SymbolTable::new(),
|
||||||
function_map: BTreeMap::new(),
|
function_map: BTreeMap::new(),
|
||||||
|
builtin_functions: BTreeSet::new(),
|
||||||
functions: vec![],
|
functions: vec![],
|
||||||
current_func_return_type: None,
|
current_func_return_type: None,
|
||||||
diagnostic: Diagnositics::new(),
|
diagnostic: Diagnositics::new(),
|
||||||
@@ -39,7 +41,10 @@ impl Analyzer {
|
|||||||
};
|
};
|
||||||
analyzer.declare_builtin_func("putint", vec![SemaType::I32], SemaType::Void);
|
analyzer.declare_builtin_func("putint", vec![SemaType::I32], SemaType::Void);
|
||||||
analyzer.declare_builtin_func("putch", vec![SemaType::I32], SemaType::Void);
|
analyzer.declare_builtin_func("putch", vec![SemaType::I32], SemaType::Void);
|
||||||
|
analyzer.declare_builtin_func("putarray", vec![SemaType::I32, SemaType::Array(Box::new(SemaType::I32), vec![0])], SemaType::Void);
|
||||||
analyzer.declare_builtin_func("getint", vec![], SemaType::I32);
|
analyzer.declare_builtin_func("getint", vec![], SemaType::I32);
|
||||||
|
analyzer.declare_builtin_func("getch", vec![], SemaType::I32);
|
||||||
|
analyzer.declare_builtin_func("getarray", vec![SemaType::Array(Box::new(SemaType::I32), vec![0])], SemaType::I32);
|
||||||
analyzer
|
analyzer
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,6 +77,7 @@ impl Analyzer {
|
|||||||
parameter_types,
|
parameter_types,
|
||||||
};
|
};
|
||||||
self.function_map.insert(name.to_string(), id);
|
self.function_map.insert(name.to_string(), id);
|
||||||
|
self.builtin_functions.insert(id);
|
||||||
self.functions.push(sig);
|
self.functions.push(sig);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,14 +104,50 @@ impl Analyzer {
|
|||||||
|
|
||||||
fn analyze_var_decl(&mut self, var_decl: VarDeclStmt, kind: SymbolKind) -> HirVarDeclStmt {
|
fn analyze_var_decl(&mut self, var_decl: VarDeclStmt, kind: SymbolKind) -> HirVarDeclStmt {
|
||||||
let base_type: SemaType = var_decl.data_type.into();
|
let base_type: SemaType = var_decl.data_type.into();
|
||||||
|
let is_static_local = kind == SymbolKind::Local && var_decl.storage_class == Some(StorageClass::Static);
|
||||||
|
let has_static_storage = kind == SymbolKind::Global || is_static_local;
|
||||||
|
let actual_kind = if is_static_local {
|
||||||
|
SymbolKind::StaticLocal
|
||||||
|
} else {
|
||||||
|
kind
|
||||||
|
};
|
||||||
let mut values = vec![];
|
let mut values = vec![];
|
||||||
for value in var_decl.values {
|
for value in var_decl.values {
|
||||||
let data_type = self.build_var_type(base_type.clone(), &value.dimensions, false);
|
let data_type = self.build_var_type(base_type.clone(), &value.dimensions, false);
|
||||||
match self.symbols.declare_variable(&value.name, kind, data_type) {
|
let initializer_is_not_constant = has_static_storage
|
||||||
Ok(symbol) => values.push(HirVarDeclStmtValue {
|
&& value
|
||||||
|
.value
|
||||||
|
.as_ref()
|
||||||
|
.is_some_and(|expr| Self::eval_const_expr(expr).is_none());
|
||||||
|
if initializer_is_not_constant {
|
||||||
|
if let Some(expr) = &value.value {
|
||||||
|
self.add_error(SemaError::InitializerNotConstant, expr.span);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match self.symbols.declare_variable(&value.name, actual_kind, data_type) {
|
||||||
|
Ok(symbol) => {
|
||||||
|
let symbol_type = self.symbols.get_symbol(symbol).ty.clone();
|
||||||
|
let init = if initializer_is_not_constant {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
value.value.and_then(|expr| {
|
||||||
|
let span = expr.span;
|
||||||
|
let init = self.analyze_expr(expr)?;
|
||||||
|
if init.ty == SemaType::Void {
|
||||||
|
self.add_error(SemaError::InvalidOperand(SemaType::Void), span);
|
||||||
|
} else if !self.type_matches(&symbol_type, &init.ty) {
|
||||||
|
self.add_error(SemaError::TypeMismatch(symbol_type.clone(), init.ty.clone()), span);
|
||||||
|
}
|
||||||
|
Some(init)
|
||||||
|
})
|
||||||
|
};
|
||||||
|
values.push(HirVarDeclStmtValue {
|
||||||
symbol,
|
symbol,
|
||||||
name_span: value.name_span,
|
name_span: value.name_span,
|
||||||
}),
|
value: init,
|
||||||
|
is_static_local,
|
||||||
|
});
|
||||||
|
}
|
||||||
Err(e) => self.add_error(e, value.name_span),
|
Err(e) => self.add_error(e, value.name_span),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -117,7 +159,11 @@ impl Analyzer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn analyze_func_decl(&mut self, func_decl: FuncDeclStmt) -> Option<HirFuncDeclStmt> {
|
fn analyze_func_decl(&mut self, func_decl: FuncDeclStmt) -> Option<HirFuncDeclStmt> {
|
||||||
if self.function_map.contains_key(&func_decl.name) {
|
let is_static = func_decl.storage_class == Some(StorageClass::Static);
|
||||||
|
let existing_function = self.function_map.get(&func_decl.name).cloned();
|
||||||
|
let can_shadow_builtin = is_static
|
||||||
|
&& existing_function.is_some_and(|function| self.builtin_functions.contains(&function));
|
||||||
|
if existing_function.is_some() && !can_shadow_builtin {
|
||||||
self.add_error(SemaError::FunctionHasBeenDefined(func_decl.name.clone()), func_decl.name_span);
|
self.add_error(SemaError::FunctionHasBeenDefined(func_decl.name.clone()), func_decl.name_span);
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
@@ -128,9 +174,14 @@ impl Analyzer {
|
|||||||
parameter_types.push(self.build_var_type(param.param_type.into(), ¶m.dimensions, true));
|
parameter_types.push(self.build_var_type(param.param_type.into(), ¶m.dimensions, true));
|
||||||
}
|
}
|
||||||
let return_type: SemaType = func_decl.return_type.into();
|
let return_type: SemaType = func_decl.return_type.into();
|
||||||
|
let ir_name = if is_static {
|
||||||
|
format!("__static_func_{}_{}", function_id.0, func_decl.name)
|
||||||
|
} else {
|
||||||
|
func_decl.name.clone()
|
||||||
|
};
|
||||||
let sig = FunctionSig {
|
let sig = FunctionSig {
|
||||||
id: function_id,
|
id: function_id,
|
||||||
name: func_decl.name.clone(),
|
name: ir_name,
|
||||||
return_type: return_type.clone(),
|
return_type: return_type.clone(),
|
||||||
parameter_types,
|
parameter_types,
|
||||||
};
|
};
|
||||||
@@ -167,6 +218,47 @@ impl Analyzer {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn eval_const_expr(expr: &Expr) -> Option<i32> {
|
||||||
|
match &expr.value {
|
||||||
|
ExprValue::IntLit(value) => Some(*value as i32),
|
||||||
|
ExprValue::UnaryOp { op, operand } => {
|
||||||
|
let value = Self::eval_const_expr(operand)?;
|
||||||
|
match op {
|
||||||
|
crate::ast::types::UnaryOp::Add => Some(value),
|
||||||
|
crate::ast::types::UnaryOp::Sub => Some(value.wrapping_neg()),
|
||||||
|
crate::ast::types::UnaryOp::Not => Some((value == 0) as i32),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ExprValue::BinaryOp { lhs, op, rhs } => {
|
||||||
|
let lhs = Self::eval_const_expr(lhs)?;
|
||||||
|
match op {
|
||||||
|
BinaryOp::And if lhs == 0 => return Some(0),
|
||||||
|
BinaryOp::Or if lhs != 0 => return Some(1),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
let rhs = Self::eval_const_expr(rhs)?;
|
||||||
|
match op {
|
||||||
|
BinaryOp::Add => Some(lhs.wrapping_add(rhs)),
|
||||||
|
BinaryOp::Sub => Some(lhs.wrapping_sub(rhs)),
|
||||||
|
BinaryOp::Mul => Some(lhs.wrapping_mul(rhs)),
|
||||||
|
BinaryOp::Div if rhs != 0 => Some(lhs.wrapping_div(rhs)),
|
||||||
|
BinaryOp::Div => None,
|
||||||
|
BinaryOp::Mod if rhs != 0 => Some(lhs.wrapping_rem(rhs)),
|
||||||
|
BinaryOp::Mod => None,
|
||||||
|
BinaryOp::Equal => Some((lhs == rhs) as i32),
|
||||||
|
BinaryOp::NotEqual => Some((lhs != rhs) as i32),
|
||||||
|
BinaryOp::Less => Some((lhs < rhs) as i32),
|
||||||
|
BinaryOp::LessEqual => Some((lhs <= rhs) as i32),
|
||||||
|
BinaryOp::Greater => Some((lhs > rhs) as i32),
|
||||||
|
BinaryOp::GreaterEqual => Some((lhs >= rhs) as i32),
|
||||||
|
BinaryOp::And => Some((lhs != 0 && rhs != 0) as i32),
|
||||||
|
BinaryOp::Or => Some((lhs != 0 || rhs != 0) as i32),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn build_var_type(&mut self, base_type: SemaType, dimensions: &[ArrayDimension], is_param: bool) -> SemaType {
|
fn build_var_type(&mut self, base_type: SemaType, dimensions: &[ArrayDimension], is_param: bool) -> SemaType {
|
||||||
if dimensions.is_empty() {
|
if dimensions.is_empty() {
|
||||||
return base_type;
|
return base_type;
|
||||||
@@ -175,30 +267,21 @@ impl Analyzer {
|
|||||||
for (i, dimension) in dimensions.iter().enumerate() {
|
for (i, dimension) in dimensions.iter().enumerate() {
|
||||||
match &dimension.value {
|
match &dimension.value {
|
||||||
Some(expr) => {
|
Some(expr) => {
|
||||||
if let ExprValue::IntLit(value) = expr.value {
|
if let Some(value) = Self::eval_const_expr(expr) {
|
||||||
if value > 0 {
|
if value > 0 {
|
||||||
dims.push(value as usize);
|
dims.push(value as usize);
|
||||||
} else {
|
continue;
|
||||||
self.add_error(SemaError::InvalidArrayDimension, dimension.span);
|
|
||||||
}
|
}
|
||||||
|
self.add_error(SemaError::InvalidArrayDimension, dimension.span);
|
||||||
} else {
|
} else {
|
||||||
self.add_error(SemaError::InvalidArrayDimension, dimension.span);
|
self.add_error(SemaError::InvalidArrayDimension, dimension.span);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None if is_param && i == 0 => {}
|
None if is_param && i == 0 => dims.push(0),
|
||||||
None => self.add_error(SemaError::InvalidArrayDimension, dimension.span),
|
None => self.add_error(SemaError::InvalidArrayDimension, dimension.span),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if is_param {
|
|
||||||
let pointee_type = if dims.is_empty() {
|
|
||||||
base_type
|
|
||||||
} else {
|
|
||||||
SemaType::Array(Box::new(base_type), dims)
|
SemaType::Array(Box::new(base_type), dims)
|
||||||
};
|
|
||||||
SemaType::Ptr(Box::new(pointee_type))
|
|
||||||
} else {
|
|
||||||
SemaType::Array(Box::new(base_type), dims)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn analyze_block_stmt(&mut self, block_stmt: BlockStmt) -> HirBlockStmt {
|
fn analyze_block_stmt(&mut self, block_stmt: BlockStmt) -> HirBlockStmt {
|
||||||
@@ -211,11 +294,19 @@ impl Analyzer {
|
|||||||
HirBlockStmt { statements }
|
HirBlockStmt { statements }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn analyze_scoped_block_stmt(&mut self, block_stmt: BlockStmt) -> HirBlockStmt {
|
||||||
|
self.symbols.enter_scope();
|
||||||
|
let block = self.analyze_block_stmt(block_stmt);
|
||||||
|
self.symbols.exit_scope();
|
||||||
|
block
|
||||||
|
}
|
||||||
|
|
||||||
fn analyze_statement(&mut self, stmt: Statement) -> Option<HirStatement> {
|
fn analyze_statement(&mut self, stmt: Statement) -> Option<HirStatement> {
|
||||||
match stmt {
|
match stmt {
|
||||||
Statement::Return(stmt) => Some(HirStatement::Return(self.analyze_return_stmt(stmt))),
|
Statement::Return(stmt) => Some(HirStatement::Return(self.analyze_return_stmt(stmt))),
|
||||||
Statement::If(stmt) => Some(HirStatement::If(self.analyze_if_stmt(stmt))),
|
Statement::If(stmt) => Some(HirStatement::If(self.analyze_if_stmt(stmt))),
|
||||||
Statement::While(stmt) => Some(HirStatement::While(self.analyze_while_stmt(stmt))),
|
Statement::While(stmt) => Some(HirStatement::While(self.analyze_while_stmt(stmt))),
|
||||||
|
Statement::For(stmt) => Some(HirStatement::For(self.analyze_for_stmt(stmt))),
|
||||||
Statement::Break(stmt) => Some(HirStatement::Break(self.analyze_break_stmt(stmt))),
|
Statement::Break(stmt) => Some(HirStatement::Break(self.analyze_break_stmt(stmt))),
|
||||||
Statement::Continue(stmt) => Some(HirStatement::Continue(self.analyze_continue_stmt(stmt))),
|
Statement::Continue(stmt) => Some(HirStatement::Continue(self.analyze_continue_stmt(stmt))),
|
||||||
Statement::Block(stmt) => {
|
Statement::Block(stmt) => {
|
||||||
@@ -265,16 +356,16 @@ impl Analyzer {
|
|||||||
|
|
||||||
fn analyze_if_stmt(&mut self, if_stmt: IfStmt) -> HirIfStmt {
|
fn analyze_if_stmt(&mut self, if_stmt: IfStmt) -> HirIfStmt {
|
||||||
let condition = self.analyze_condition_expr(if_stmt.condition);
|
let condition = self.analyze_condition_expr(if_stmt.condition);
|
||||||
let then_branch = self.analyze_block_stmt(if_stmt.then_branch);
|
let then_branch = self.analyze_scoped_block_stmt(if_stmt.then_branch);
|
||||||
let mut ifelse_branch = vec![];
|
let mut ifelse_branch = vec![];
|
||||||
for branch in if_stmt.ifelse_branch {
|
for branch in if_stmt.ifelse_branch {
|
||||||
let IfElseBranch { condition, then_branch } = branch;
|
let IfElseBranch { condition, then_branch } = branch;
|
||||||
ifelse_branch.push(HirIfElseBranch {
|
ifelse_branch.push(HirIfElseBranch {
|
||||||
condition: self.analyze_condition_expr(condition),
|
condition: self.analyze_condition_expr(condition),
|
||||||
then_branch: self.analyze_block_stmt(then_branch),
|
then_branch: self.analyze_scoped_block_stmt(then_branch),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
let else_branch = if_stmt.else_branch.map(|block| self.analyze_block_stmt(block));
|
let else_branch = if_stmt.else_branch.map(|block| self.analyze_scoped_block_stmt(block));
|
||||||
HirIfStmt {
|
HirIfStmt {
|
||||||
condition,
|
condition,
|
||||||
then_branch,
|
then_branch,
|
||||||
@@ -286,7 +377,7 @@ impl Analyzer {
|
|||||||
fn analyze_while_stmt(&mut self, while_stmt: WhileStmt) -> HirWhileStmt {
|
fn analyze_while_stmt(&mut self, while_stmt: WhileStmt) -> HirWhileStmt {
|
||||||
let condition = self.analyze_condition_expr(while_stmt.condition);
|
let condition = self.analyze_condition_expr(while_stmt.condition);
|
||||||
self.loop_depth += 1;
|
self.loop_depth += 1;
|
||||||
let body = self.analyze_block_stmt(while_stmt.body);
|
let body = self.analyze_scoped_block_stmt(while_stmt.body);
|
||||||
self.loop_depth -= 1;
|
self.loop_depth -= 1;
|
||||||
HirWhileStmt {
|
HirWhileStmt {
|
||||||
condition,
|
condition,
|
||||||
@@ -294,6 +385,26 @@ impl Analyzer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn analyze_for_stmt(&mut self, for_stmt: ForStmt) -> HirForStmt {
|
||||||
|
self.symbols.enter_scope();
|
||||||
|
let init = for_stmt.init.and_then(|init| match init {
|
||||||
|
ForInit::Expr(expr) => self.analyze_expr(expr).map(HirForInit::Expr),
|
||||||
|
ForInit::VarDecl(var_decl) => Some(HirForInit::VarDecl(self.analyze_var_decl(var_decl, SymbolKind::Local))),
|
||||||
|
});
|
||||||
|
let condition = for_stmt.condition.map(|expr| self.analyze_condition_expr(expr));
|
||||||
|
let update = for_stmt.update.and_then(|expr| self.analyze_expr(expr));
|
||||||
|
self.loop_depth += 1;
|
||||||
|
let body = self.analyze_scoped_block_stmt(for_stmt.body);
|
||||||
|
self.loop_depth -= 1;
|
||||||
|
self.symbols.exit_scope();
|
||||||
|
HirForStmt {
|
||||||
|
init,
|
||||||
|
condition,
|
||||||
|
update,
|
||||||
|
body,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn analyze_break_stmt(&mut self, stmt: BreakStmt) -> HirBreakStmt {
|
fn analyze_break_stmt(&mut self, stmt: BreakStmt) -> HirBreakStmt {
|
||||||
if self.loop_depth == 0 {
|
if self.loop_depth == 0 {
|
||||||
self.add_error(SemaError::BreakOutsideLoop, stmt.span);
|
self.add_error(SemaError::BreakOutsideLoop, stmt.span);
|
||||||
@@ -348,6 +459,7 @@ impl Analyzer {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
ExprValue::Assign { lvalue, rvalue } => self.analyze_assign_expr(*lvalue, *rvalue, span),
|
ExprValue::Assign { lvalue, rvalue } => self.analyze_assign_expr(*lvalue, *rvalue, span),
|
||||||
|
ExprValue::CompoundAssign { lvalue, op, rvalue } => self.analyze_compound_assign_expr(*lvalue, op, *rvalue, span),
|
||||||
ExprValue::ArrayAccess { array, index } => self.analyze_array_access_expr(*array, *index, span),
|
ExprValue::ArrayAccess { array, index } => self.analyze_array_access_expr(*array, *index, span),
|
||||||
ExprValue::UnaryOp { op, operand } => {
|
ExprValue::UnaryOp { op, operand } => {
|
||||||
let operand_span = operand.span;
|
let operand_span = operand.span;
|
||||||
@@ -358,6 +470,7 @@ impl Analyzer {
|
|||||||
}
|
}
|
||||||
let ty = match op {
|
let ty = match op {
|
||||||
crate::ast::types::UnaryOp::Not => SemaType::I1,
|
crate::ast::types::UnaryOp::Not => SemaType::I1,
|
||||||
|
crate::ast::types::UnaryOp::Add | crate::ast::types::UnaryOp::Sub if operand.ty == SemaType::I1 => SemaType::I32,
|
||||||
_ => operand.ty.clone(),
|
_ => operand.ty.clone(),
|
||||||
};
|
};
|
||||||
Some(HirExpr {
|
Some(HirExpr {
|
||||||
@@ -369,6 +482,27 @@ impl Analyzer {
|
|||||||
span,
|
span,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
ExprValue::IncDec { op, operand, is_prefix } => {
|
||||||
|
if !matches!(operand.value, ExprValue::Var(_) | ExprValue::ArrayAccess { .. }) {
|
||||||
|
self.add_error(SemaError::InvalidAssignmentTarget, operand.span);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let operand_span = operand.span;
|
||||||
|
let operand = self.analyze_expr(*operand)?;
|
||||||
|
if !operand.ty.is_scalar() {
|
||||||
|
self.add_error(SemaError::InvalidOperand(operand.ty.clone()), operand_span);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(HirExpr {
|
||||||
|
ty: operand.ty.clone(),
|
||||||
|
value: HirExprValue::IncDec {
|
||||||
|
op,
|
||||||
|
operand: Box::new(operand),
|
||||||
|
is_prefix,
|
||||||
|
},
|
||||||
|
span,
|
||||||
|
})
|
||||||
|
}
|
||||||
ExprValue::BinaryOp { lhs, op, rhs } => self.analyze_binary_expr(*lhs, op, *rhs, span),
|
ExprValue::BinaryOp { lhs, op, rhs } => self.analyze_binary_expr(*lhs, op, *rhs, span),
|
||||||
ExprValue::FuncCall(func_name, args) => self.analyze_func_call_expr(func_name, args, span),
|
ExprValue::FuncCall(func_name, args) => self.analyze_func_call_expr(func_name, args, span),
|
||||||
}
|
}
|
||||||
@@ -404,6 +538,50 @@ impl Analyzer {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn analyze_compound_assign_expr(&mut self, lvalue: Expr, op: BinaryOp, rvalue: Expr, span: Span) -> Option<HirExpr> {
|
||||||
|
if !matches!(lvalue.value, ExprValue::Var(_) | ExprValue::ArrayAccess { .. }) {
|
||||||
|
self.add_error(SemaError::InvalidAssignmentTarget, lvalue.span);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let lvalue_span = lvalue.span;
|
||||||
|
let lvalue = self.analyze_expr(lvalue)?;
|
||||||
|
if lvalue.ty.is_array_like() {
|
||||||
|
self.add_error(SemaError::InvalidAssignmentTarget, lvalue.span);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let rvalue_span = rvalue.span;
|
||||||
|
let rvalue = self.analyze_expr(rvalue)?;
|
||||||
|
if !lvalue.ty.is_scalar() {
|
||||||
|
self.add_error(SemaError::InvalidOperand(lvalue.ty.clone()), lvalue_span);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
if !rvalue.ty.is_scalar() {
|
||||||
|
self.add_error(SemaError::InvalidOperand(rvalue.ty.clone()), rvalue_span);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let result_ty = match SemaType::get_elevate_result(&lvalue.ty, &rvalue.ty) {
|
||||||
|
Some(ty) => ty,
|
||||||
|
None => {
|
||||||
|
self.add_error(SemaError::IncompatiableOperand(lvalue.ty.clone(), rvalue.ty.clone()), lvalue_span);
|
||||||
|
self.add_error(SemaError::IncompatiableOperand(lvalue.ty.clone(), rvalue.ty.clone()), rvalue_span);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if !self.type_matches(&lvalue.ty, &result_ty) {
|
||||||
|
self.add_error(SemaError::TypeMismatch(lvalue.ty.clone(), result_ty), span);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(HirExpr {
|
||||||
|
ty: lvalue.ty.clone(),
|
||||||
|
value: HirExprValue::CompoundAssign {
|
||||||
|
lvalue: Box::new(lvalue),
|
||||||
|
op,
|
||||||
|
rvalue: Box::new(rvalue),
|
||||||
|
},
|
||||||
|
span,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn analyze_binary_expr(&mut self, lhs: Expr, op: BinaryOp, rhs: Expr, span: Span) -> Option<HirExpr> {
|
fn analyze_binary_expr(&mut self, lhs: Expr, op: BinaryOp, rhs: Expr, span: Span) -> Option<HirExpr> {
|
||||||
let lhs_span = lhs.span;
|
let lhs_span = lhs.span;
|
||||||
let rhs_span = rhs.span;
|
let rhs_span = rhs.span;
|
||||||
@@ -506,7 +684,7 @@ impl Analyzer {
|
|||||||
let ty = match array.ty.indexed_type() {
|
let ty = match array.ty.indexed_type() {
|
||||||
Some(ty) => ty,
|
Some(ty) => ty,
|
||||||
None => {
|
None => {
|
||||||
self.add_error(SemaError::NotSubscriptable, span);
|
self.add_error(SemaError::NotSubscriptable(array.ty.clone()), span);
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -524,6 +702,14 @@ impl Analyzer {
|
|||||||
if expected == actual {
|
if expected == actual {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if matches!((expected, actual), (SemaType::I32, SemaType::I1)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if let (SemaType::Array(expected_elem, expected_dims), SemaType::Array(actual_elem, actual_dims)) = (expected, actual) {
|
||||||
|
return expected_elem == actual_elem
|
||||||
|
&& expected_dims.len() == actual_dims.len()
|
||||||
|
&& expected_dims.iter().zip(actual_dims.iter()).all(|(expected_dim, actual_dim)| *expected_dim == 0 || expected_dim == actual_dim);
|
||||||
|
}
|
||||||
matches!((expected, actual), (SemaType::Ptr(expected_elem), SemaType::Array(_, _)) if actual.indexed_type().is_some_and(|ty| &ty == expected_elem.as_ref()))
|
matches!((expected, actual), (SemaType::Ptr(expected_elem), SemaType::Array(_, _)) if actual.indexed_type().is_some_and(|ty| &ty == expected_elem.as_ref()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-2
@@ -34,6 +34,8 @@ pub enum SemaError {
|
|||||||
ReturnExpressionOnVoidFunction,
|
ReturnExpressionOnVoidFunction,
|
||||||
#[error("array dimension must be a positive integer literal")]
|
#[error("array dimension must be a positive integer literal")]
|
||||||
InvalidArrayDimension,
|
InvalidArrayDimension,
|
||||||
#[error("subscripted value is not an array or pointer")]
|
#[error("subscripted value of type `{0}` is not an array or pointer")]
|
||||||
NotSubscriptable,
|
NotSubscriptable(SemaType),
|
||||||
|
#[error("initializer element is not constant")]
|
||||||
|
InitializerNotConstant,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ pub struct HirVarDeclStmt {
|
|||||||
pub struct HirVarDeclStmtValue {
|
pub struct HirVarDeclStmtValue {
|
||||||
pub symbol: SymbolId,
|
pub symbol: SymbolId,
|
||||||
pub name_span: Span,
|
pub name_span: Span,
|
||||||
|
pub value: Option<HirExpr>,
|
||||||
|
pub is_static_local: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct HirFuncDeclStmt {
|
pub struct HirFuncDeclStmt {
|
||||||
@@ -46,6 +48,7 @@ pub enum HirStatement {
|
|||||||
VarDecl(HirVarDeclStmt),
|
VarDecl(HirVarDeclStmt),
|
||||||
If(HirIfStmt),
|
If(HirIfStmt),
|
||||||
While(HirWhileStmt),
|
While(HirWhileStmt),
|
||||||
|
For(HirForStmt),
|
||||||
Break(HirBreakStmt),
|
Break(HirBreakStmt),
|
||||||
Continue(HirContinueStmt),
|
Continue(HirContinueStmt),
|
||||||
}
|
}
|
||||||
@@ -67,6 +70,18 @@ pub struct HirWhileStmt {
|
|||||||
pub body: HirBlockStmt,
|
pub body: HirBlockStmt,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct HirForStmt {
|
||||||
|
pub init: Option<HirForInit>,
|
||||||
|
pub condition: Option<HirExpr>,
|
||||||
|
pub update: Option<HirExpr>,
|
||||||
|
pub body: HirBlockStmt,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum HirForInit {
|
||||||
|
Expr(HirExpr),
|
||||||
|
VarDecl(HirVarDeclStmt),
|
||||||
|
}
|
||||||
|
|
||||||
pub struct HirBreakStmt {
|
pub struct HirBreakStmt {
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
}
|
}
|
||||||
@@ -102,9 +117,19 @@ pub enum HirExprValue {
|
|||||||
op: UnaryOp,
|
op: UnaryOp,
|
||||||
operand: Box<HirExpr>,
|
operand: Box<HirExpr>,
|
||||||
},
|
},
|
||||||
|
IncDec {
|
||||||
|
op: crate::ast::types::IncDecOp,
|
||||||
|
operand: Box<HirExpr>,
|
||||||
|
is_prefix: bool,
|
||||||
|
},
|
||||||
FuncCall(FunctionId, Vec<HirExpr>),
|
FuncCall(FunctionId, Vec<HirExpr>),
|
||||||
Assign {
|
Assign {
|
||||||
lvalue: Box<HirExpr>,
|
lvalue: Box<HirExpr>,
|
||||||
rvalue: Box<HirExpr>,
|
rvalue: Box<HirExpr>,
|
||||||
},
|
},
|
||||||
|
CompoundAssign {
|
||||||
|
lvalue: Box<HirExpr>,
|
||||||
|
op: BinaryOp,
|
||||||
|
rvalue: Box<HirExpr>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ pub enum SymbolKind {
|
|||||||
Global,
|
Global,
|
||||||
Local,
|
Local,
|
||||||
Param,
|
Param,
|
||||||
|
StaticLocal,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
|||||||
+5
-2
@@ -13,10 +13,11 @@ pub enum SemaType {
|
|||||||
|
|
||||||
impl SemaType {
|
impl SemaType {
|
||||||
pub fn get_elevate_result(lhs: &SemaType, rhs: &SemaType) -> Option<SemaType> {
|
pub fn get_elevate_result(lhs: &SemaType, rhs: &SemaType) -> Option<SemaType> {
|
||||||
|
if matches!((lhs, rhs), (SemaType::I32 | SemaType::I1, SemaType::I32 | SemaType::I1)) {
|
||||||
|
return Some(SemaType::I32);
|
||||||
|
}
|
||||||
if lhs == rhs {
|
if lhs == rhs {
|
||||||
Some(lhs.clone())
|
Some(lhs.clone())
|
||||||
} else if (*lhs == SemaType::I32 && *rhs == SemaType::I1) || (*lhs == SemaType::I1 && *rhs == SemaType::I32) {
|
|
||||||
Some(SemaType::I32)
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@@ -35,6 +36,8 @@ impl SemaType {
|
|||||||
SemaType::Array(elem, dims) => {
|
SemaType::Array(elem, dims) => {
|
||||||
if dims.len() == 1 {
|
if dims.len() == 1 {
|
||||||
Some((**elem).clone())
|
Some((**elem).clone())
|
||||||
|
} else if dims.first().is_some_and(|dim| *dim == 0) {
|
||||||
|
Some(SemaType::Array(elem.clone(), dims[1..].to_vec()))
|
||||||
} else {
|
} else {
|
||||||
Some(SemaType::Array(elem.clone(), dims[1..].to_vec()))
|
Some(SemaType::Array(elem.clone(), dims[1..].to_vec()))
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user