From 781c35d4846b8cae5cc69d4e133331918db9cbf1 Mon Sep 17 00:00:00 2001 From: Toni Date: Thu, 25 Dec 2025 15:19:15 +0100 Subject: [PATCH] basic global constants --- src/analyzer.rs | 14 ++++++++++++++ src/codegen_x86_64.rs | 38 ++++++++++++++++++++++++++------------ src/main.rs | 1 + src/parser.rs | 14 ++++++++++++++ src/tokenizer.rs | 2 ++ 5 files changed, 57 insertions(+), 12 deletions(-) diff --git a/src/analyzer.rs b/src/analyzer.rs index 76e8ed8..ad2dfb1 100644 --- a/src/analyzer.rs +++ b/src/analyzer.rs @@ -7,12 +7,14 @@ use crate::{ pub struct Analyzer { pub functions: HashMap, + pub constants: HashMap, } impl Analyzer { pub fn new() -> Analyzer { Analyzer { functions: HashMap::new(), + constants: HashMap::new(), } } @@ -44,6 +46,18 @@ impl Analyzer { } => { self.analyze_expr(initializer)?; } + Stmt::Const { name, value } => { + if self.constants.contains_key(&name.lexeme) + || self.functions.contains_key(&name.lexeme) + { + return error!( + name.loc, + format!("tried to redefine constant '{}'", name.lexeme) + ); + } + self.constants + .insert(name.lexeme.clone(), value.lexeme.parse().unwrap()); + } Stmt::Block(statements) => { for stmt in statements { self.analyze_stmt(stmt)?; diff --git a/src/codegen_x86_64.rs b/src/codegen_x86_64.rs index 04363c8..31eec08 100644 --- a/src/codegen_x86_64.rs +++ b/src/codegen_x86_64.rs @@ -176,6 +176,9 @@ _builtin_environ: let offset = env.define_var(name.lexeme.clone(), var_type); emit!(&mut self.output, " mov QWORD [rbp-{}], rax", offset); } + Stmt::Const { name: _, value: _ } => { + // handled in the analyzer + } Stmt::Block(statements) => { env.push_scope(); for stmt in statements { @@ -445,18 +448,29 @@ _builtin_environ: } } Expr::Variable(name) => { - // TODO: move to analyzer - let var = match env.get_var(&name.lexeme) { - Some(x) => x, - None => { - return error!(name.loc, format!("undefined variable: {}", &name.lexeme)); - } - }; - emit!( - &mut self.output, - " mov rax, QWORD [rbp-{}]", - var.stack_offset, - ); + if self.analyzer.constants.contains_key(&name.lexeme) { + emit!( + &mut self.output, + " mov rax, {}", + self.analyzer.constants[&name.lexeme] + ); + } else { + // TODO: move to analyzer + let var = match env.get_var(&name.lexeme) { + Some(x) => x, + None => { + return error!( + name.loc, + format!("undefined variable: {}", &name.lexeme) + ); + } + }; + emit!( + &mut self.output, + " mov rax, QWORD [rbp-{}]", + var.stack_offset, + ); + } } Expr::Assign { name, value } => { self.compile_expr(env, *value)?; diff --git a/src/main.rs b/src/main.rs index 4e5f7b8..a0182d6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -33,6 +33,7 @@ fn compile_file_to( } for stmt in statements { + // top level statements are all function/const/extern declarations so a new env for each codegen.compile_stmt(&mut codegen_x86_64::Env::new(), stmt)?; } Ok(()) diff --git a/src/parser.rs b/src/parser.rs index 999fbce..1569941 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -14,6 +14,10 @@ pub enum Stmt { var_type: Option, initializer: Expr, }, + Const { + name: Token, + value: Token, + }, Block(Vec), If { condition: Expr, @@ -115,6 +119,9 @@ impl Parser { if self.match_token(&[TokenType::KeywordExtern]) { return self.extern_declaration(); } + if self.match_token(&[TokenType::KeywordConst]) { + return self.const_declaration(); + } return error!( self.peek().loc, "statements not allowed outside function body" @@ -192,6 +199,13 @@ impl Parser { }) } + fn const_declaration(&mut self) -> Result { + let name = self.consume(TokenType::Identifier, "expected const name")?; + self.consume(TokenType::Equal, "expected '=' after const name")?; + let value = self.consume(TokenType::Number, "expected a number after '='")?; + Ok(Stmt::Const { name, value }) + } + fn extern_declaration(&mut self) -> Result { Ok(Stmt::Extern( self.consume(TokenType::Identifier, "expected extern name")?, diff --git a/src/tokenizer.rs b/src/tokenizer.rs index d86c659..a96874b 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -39,6 +39,7 @@ pub enum TokenType { False, KeywordLet, + KeywordConst, KeywordIf, KeywordElse, KeywordWhile, @@ -336,6 +337,7 @@ impl Tokenizer { let lexeme: String = self.source[self.start..self.current].iter().collect(); self.add_token(match lexeme.as_str() { "let" => TokenType::KeywordLet, + "const" => TokenType::KeywordConst, "if" => TokenType::KeywordIf, "else" => TokenType::KeywordElse, "while" => TokenType::KeywordWhile,