diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..064aabb --- /dev/null +++ b/.vscode/launch.json @@ -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" + ] + } + } + ] +} \ No newline at end of file diff --git a/src/ast/_types/typ.rs b/src/ast/_types/typ.rs new file mode 100644 index 0000000..4048060 --- /dev/null +++ b/src/ast/_types/typ.rs @@ -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, + dimensions: Vec, +} + +pub struct ArrayDimension { + span: Span, + size: Option, +} \ No newline at end of file diff --git a/src/ast/parser/decl.rs b/src/ast/parser/decl.rs index 9ce2eab..a291ce2 100644 --- a/src/ast/parser/decl.rs +++ b/src/ast/parser/decl.rs @@ -1,8 +1,8 @@ use crate::{ ast::err::ParseError, ast::types::{ - ArrayDimension, CompileUnit, Expr, FuncDeclStmt, GlobalDeclStmt, Param, VarDeclStmt, - VarDeclStmtValue, + ArrayDimension, CompileUnit, Expr, FuncDeclStmt, GlobalDeclStmt, Param, StorageClass, + VarDeclStmt, VarDeclStmtValue, }, diagnostic::span::Span, lexer::types::{TokenValue, TypeIdent}, @@ -10,13 +10,8 @@ use crate::{ use super::{ParseProcessError, ParseType, Parser}; -#[derive(Clone, Copy)] -enum StorageClass { - Static, -} - struct DeclSpecifiers { - _storage_class: Option, + storage_class: Option, type_specifier: TypeIdent, type_span: Span, } @@ -111,7 +106,7 @@ impl Parser { }; Ok(DeclSpecifiers { - _storage_class: storage_class, + storage_class, type_specifier, type_span: type_token.span, }) @@ -183,13 +178,13 @@ impl Parser { self.advance(1); span } - TokenValue::Eof => { - self.diagnostics.add_from_frontend_error( - ParseError::ExpectedBefore(TokenValue::Eof, "`]`"), - start_span, - ); - return Err(ParseProcessError::ErrorInMatch); - } + // 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( @@ -279,6 +274,7 @@ impl Parser { let body = self.parse_block_stmt(ParseType::MustParse)?; Ok(FuncDeclStmt { return_type: specifiers.type_specifier.into(), + storage_class: specifiers.storage_class, name, params, body, @@ -292,21 +288,10 @@ impl Parser { specifiers: DeclSpecifiers, first_declarator: Declarator, consume_semicolon: bool, - allow_empty_declarator_list: bool, + _allow_empty_declarator_list: bool, ) -> Result { let mut values = vec![]; - if self.peek().value == TokenValue::Semicolon && allow_empty_declarator_list { - if consume_semicolon { - self.advance(1); - } - return Ok(VarDeclStmt { - values, - type_span: specifiers.type_span, - data_type: specifiers.type_specifier.into(), - }); - } - let initializer = self.parse_initializer()?; values.push(self.init_declarator_to_var_value(InitDeclarator { declarator: first_declarator, @@ -328,6 +313,7 @@ impl Parser { } Ok(VarDeclStmt { + storage_class: specifiers.storage_class, values, type_span: specifiers.type_span, data_type: specifiers.type_specifier.into(), diff --git a/src/ast/parser/expr.rs b/src/ast/parser/expr.rs index d64e1a6..7b3aeec 100644 --- a/src/ast/parser/expr.rs +++ b/src/ast/parser/expr.rs @@ -133,8 +133,8 @@ impl Parser { } else { IncDecOp::Dec }; - let end_span = self.peek_n(1).span; - self.advance(2); + let end_span = self.peek().span; + self.advance(1); expr = Expr { span: Span::from_two(expr.span, end_span), value: ExprValue::IncDec { diff --git a/src/ast/types.rs b/src/ast/types.rs index 0383f8f..5cbf27f 100644 --- a/src/ast/types.rs +++ b/src/ast/types.rs @@ -8,9 +8,13 @@ pub enum GlobalDeclStmt { VarDecl(VarDeclStmt), FuncDecl(FuncDeclStmt), } - +#[derive(Clone, Copy, PartialEq, Eq)] +pub enum StorageClass { + Static, +} pub struct VarDeclStmt { pub values: Vec, + pub storage_class: Option, pub data_type: Type, pub type_span: Span, } @@ -28,6 +32,7 @@ pub struct ArrayDimension { pub struct FuncDeclStmt { pub name: String, + pub storage_class: Option, pub return_type: Type, pub params: Vec, pub body: BlockStmt, diff --git a/src/ir/generator.rs b/src/ir/generator.rs index 281145e..59183e0 100644 --- a/src/ir/generator.rs +++ b/src/ir/generator.rs @@ -1,6 +1,6 @@ use std::{collections::BTreeMap, vec}; -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}}}; +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::UnaryOp as AstUnaryOp; 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 while_exit_label: Vec<(usize, usize)>, // continue exit, break exit func_exit: Option<(usize, Option)>, // (label, return_var) + extra_global_instrs: Vec, label_counter: usize } @@ -25,6 +26,7 @@ impl<'a> Generator<'a> { current_exit_label: vec![], while_exit_label: vec![], func_exit: None, + extra_global_instrs: vec![], label_counter: 0, } } @@ -55,7 +57,10 @@ impl<'a> Generator<'a> { instrs.extend(self.generate_var_decl(var_decl, true)); } 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); } } } @@ -65,10 +70,18 @@ impl<'a> Generator<'a> { fn generate_var_decl(&mut self, var_decl: HirVarDeclStmt, is_global: bool) -> Vec { let mut instrs = vec![]; for value in var_decl.values { - let var_type = if is_global { VariableType::Global } else { VariableType::Local }; + 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()); - if is_global { - instrs.push(IRInstr::Declare(var)); + 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 { + instrs.push(decl); + } else { + self.extra_global_instrs.push(decl); + } } else if let Some(init) = value.value { self.current_exit_label.push(None); let (init_instrs, init_var) = match self.generate_expr(init) { @@ -88,6 +101,40 @@ impl<'a> Generator<'a> { instrs } + fn const_init_value(expr: &HirExpr) -> Option { + 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 => value.checked_neg(), + AstUnaryOp::Not => Some((value == 0) as i32), + } + } + HirExprValue::BinaryOp { lhs, op, rhs } => { + let lhs = Self::const_init_value(lhs)?; + let rhs = Self::const_init_value(rhs)?; + match op { + AstBinaryOp::Add => lhs.checked_add(rhs), + AstBinaryOp::Sub => lhs.checked_sub(rhs), + AstBinaryOp::Mul => lhs.checked_mul(rhs), + AstBinaryOp::Div => lhs.checked_div(rhs), + AstBinaryOp::Mod => lhs.checked_rem(rhs), + 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 { let parameters: Vec = func_decl.params.iter() .map(|param| self.var_manager.declare_symbol(param.symbol, VariableType::Local, param.param_type.clone().into())) diff --git a/src/ir/types.rs b/src/ir/types.rs index 903ddaf..e72a364 100644 --- a/src/ir/types.rs +++ b/src/ir/types.rs @@ -21,6 +21,7 @@ impl Display for VariableOrIntLit { #[derive(Clone)] pub enum IRInstr { Declare(Variable), + DeclareGlobal(Variable, Option), DefineFunc(Function, Vec, Vec), Entry, Binary(Variable, Variable, BinaryOp, Variable), @@ -54,6 +55,8 @@ impl Display for IRInstr { IRInstr::Load(dest, addr) => write!(f, "{} = *{}", dest, addr), IRInstr::Store(addr, value) => write!(f, "*{} = {}", addr, value), 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) => { let body_str = body.iter().map(|instr| format!(" {}", instr)).collect::>().join("\n"); write!(f, "define {} {{\n{}\n}}", func.to_decl_string(args), body_str) diff --git a/src/sema/analyzer.rs b/src/sema/analyzer.rs index 0b67c33..e6b39f7 100644 --- a/src/sema/analyzer.rs +++ b/src/sema/analyzer.rs @@ -3,7 +3,7 @@ use std::collections::BTreeMap; use crate::{ ast::types::{ 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}, sema::{ @@ -41,6 +41,7 @@ impl Analyzer { 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("getch", vec![], SemaType::I32); analyzer.declare_builtin_func("getarray", vec![SemaType::Array(Box::new(SemaType::I32), vec![0])], SemaType::I32); analyzer } @@ -100,13 +101,33 @@ impl Analyzer { fn analyze_var_decl(&mut self, var_decl: VarDeclStmt, kind: SymbolKind) -> HirVarDeclStmt { 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![]; for value in var_decl.values { 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 + && 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 = value.value.and_then(|expr| { + 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 { @@ -115,11 +136,13 @@ impl Analyzer { self.add_error(SemaError::TypeMismatch(symbol_type.clone(), init.ty.clone()), span); } Some(init) - }); + }) + }; values.push(HirVarDeclStmtValue { symbol, name_span: value.name_span, value: init, + is_static_local, }); } Err(e) => self.add_error(e, value.name_span), @@ -183,6 +206,40 @@ impl Analyzer { }) } + fn eval_const_expr(expr: &Expr) -> Option { + match &expr.value { + ExprValue::IntLit(value) => i32::try_from(*value).ok(), + 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 => value.checked_neg(), + crate::ast::types::UnaryOp::Not => Some((value == 0) as i32), + } + } + ExprValue::BinaryOp { lhs, op, rhs } => { + let lhs = Self::eval_const_expr(lhs)?; + let rhs = Self::eval_const_expr(rhs)?; + match op { + BinaryOp::Add => lhs.checked_add(rhs), + BinaryOp::Sub => lhs.checked_sub(rhs), + BinaryOp::Mul => lhs.checked_mul(rhs), + BinaryOp::Div => lhs.checked_div(rhs), + BinaryOp::Mod => lhs.checked_rem(rhs), + 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 { if dimensions.is_empty() { return base_type; @@ -218,6 +275,13 @@ impl Analyzer { 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 { match stmt { Statement::Return(stmt) => Some(HirStatement::Return(self.analyze_return_stmt(stmt))), @@ -273,16 +337,16 @@ impl Analyzer { fn analyze_if_stmt(&mut self, if_stmt: IfStmt) -> HirIfStmt { 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![]; for branch in if_stmt.ifelse_branch { let IfElseBranch { condition, then_branch } = branch; ifelse_branch.push(HirIfElseBranch { 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 { condition, then_branch, @@ -294,7 +358,7 @@ impl Analyzer { fn analyze_while_stmt(&mut self, while_stmt: WhileStmt) -> HirWhileStmt { let condition = self.analyze_condition_expr(while_stmt.condition); 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; HirWhileStmt { condition, @@ -311,7 +375,7 @@ impl Analyzer { 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_block_stmt(for_stmt.body); + let body = self.analyze_scoped_block_stmt(for_stmt.body); self.loop_depth -= 1; self.symbols.exit_scope(); HirForStmt { diff --git a/src/sema/err.rs b/src/sema/err.rs index 82cdde8..d41705a 100644 --- a/src/sema/err.rs +++ b/src/sema/err.rs @@ -36,4 +36,6 @@ pub enum SemaError { InvalidArrayDimension, #[error("subscripted value is not an array or pointer")] NotSubscriptable, + #[error("initializer element is not constant")] + InitializerNotConstant, } diff --git a/src/sema/hir.rs b/src/sema/hir.rs index 04adeb5..133fbbc 100644 --- a/src/sema/hir.rs +++ b/src/sema/hir.rs @@ -19,6 +19,7 @@ pub struct HirVarDeclStmtValue { pub symbol: SymbolId, pub name_span: Span, pub value: Option, + pub is_static_local: bool, } pub struct HirFuncDeclStmt { diff --git a/src/sema/symbol.rs b/src/sema/symbol.rs index 46983ca..d927b16 100644 --- a/src/sema/symbol.rs +++ b/src/sema/symbol.rs @@ -10,6 +10,7 @@ pub enum SymbolKind { Global, Local, Param, + StaticLocal, } #[derive(Clone, Debug)]