Skip to content

Commit f8a8fa5

Browse files
authored
feat: implement static globals, type casts, pointer arithmetic, and enhanced diagnostics (#298)
This commit introduces several core language features, including global static variables, explicit type casting, and pointer arithmetic. It also significantly upgrades the compiler's resilience by introducing a panic-guarded diagnostic system and cross-platform ABI support. Changes: - **Language Features**: - **Static Globals**: Added the `static` keyword for global variables that persist throughout the program. - **Type Casting**: Implemented the `as` operator (e.g., `expr as type`) for explicit type conversions, supported in both the parser and LLVM backend. - **Pointer Arithmetic**: Added support for `ptr + int`, `ptr - int`, and `ptr - ptr` (pointer difference) using LLVM `gep` and `sub` instructions. - **Match Statement**: Finalized the `match` expression syntax and verification, ensuring no duplicate patterns in arms. - **Compiler Infrastructure**: - **Panic-Guarded Diagnostics**: Introduced a robust runner that captures backend panics and uses "source-span inference" to map low-level LLVM errors back to the original Wave source code. - **Cross-ABI Support**: Overhauled `abi_c.rs` to support target-specific calling conventions for both **Linux x86_64 (SysV)** and **macOS arm64 (Darwin)**, handling aggregate splitting and SRet rules. - **Structured Errors**: Refactored the parser to return `ParseError` objects containing detailed diagnostics (expected/found tokens, help messages, and context). - **Standard Library Updates**: - Updated the Linux syscall layer (`fs`, `memory`, `socket`, `process`) with explicit `as i64` casts to comply with stricter type-checking rules for register arguments. - Improved memory allocation safety with `null` checks and syscall error handling. - **Documentation**: - Expanded `README.md` with build instructions, target support status, and a comprehensive CLI usage guide. These updates bridge the gap between low-level system access and high-level safety, making Wave more suitable for systems programming on multiple architectures. Signed-off-by: LunaStev <luna@lunastev.org>
1 parent 8369055 commit f8a8fa5

40 files changed

+1942
-625
lines changed

README.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,49 @@ fun main() {
6060

6161
---
6262

63+
## Build From Source
64+
65+
```bash
66+
git clone https://github.com/wavefnd/Wave.git
67+
cd Wave
68+
cargo build
69+
```
70+
71+
Compiler binary path:
72+
73+
- `target/debug/wavec` (development build)
74+
- `target/release/wavec` (release build)
75+
76+
---
77+
78+
## Target Support
79+
80+
- Linux `x86_64`
81+
- macOS (Darwin) `arm64` (Apple Silicon)
82+
- Windows: not supported yet
83+
84+
---
85+
86+
## CLI Usage
87+
88+
```bash
89+
wavec run <file>
90+
wavec build <file>
91+
wavec build -o <file>
92+
wavec img <file>
93+
```
94+
95+
Useful global options:
96+
97+
- `-O0..-O3`, `-Os`, `-Oz`, `-Ofast`
98+
- `--debug-wave=tokens,ast,ir,mc,hex,all`
99+
- `--link=<lib>`
100+
- `-L <path>`
101+
- `--dep-root=<path>`
102+
- `--dep=<name>=<path>`
103+
104+
---
105+
63106
<p align="center">
64107
<a href="https://star-history.com/#wavefnd/Wave&Date">
65108
<img src="https://api.star-history.com/svg?repos=wavefnd/Wave&type=Date" alt="Star History Chart" width="80%">

front/lexer/src/ident.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,11 @@ use crate::{Lexer, Token};
1414

1515
impl<'a> Lexer<'a> {
1616
pub(crate) fn identifier(&mut self) -> String {
17-
let start = if self.current > 0 { self.current - 1 } else { 0 };
17+
let start = if self.current > 0 {
18+
self.current - 1
19+
} else {
20+
0
21+
};
1822

1923
while !self.is_at_end() {
2024
let c = self.peek();
@@ -50,6 +54,11 @@ impl<'a> Lexer<'a> {
5054
lexeme: "enum".to_string(),
5155
line: self.line,
5256
},
57+
"static" => Token {
58+
token_type: TokenType::Static,
59+
lexeme: "static".to_string(),
60+
line: self.line,
61+
},
5362
"var" => Token {
5463
token_type: TokenType::Var,
5564
lexeme: "var".to_string(),
@@ -135,6 +144,11 @@ impl<'a> Lexer<'a> {
135144
lexeme: "is".to_string(),
136145
line: self.line,
137146
},
147+
"as" => Token {
148+
token_type: TokenType::As,
149+
lexeme: "as".to_string(),
150+
line: self.line,
151+
},
138152
"asm" => Token {
139153
token_type: TokenType::Asm,
140154
lexeme: "asm".to_string(),

front/lexer/src/token.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ pub enum TokenType {
8383
Extern,
8484
Type,
8585
Enum,
86+
Static,
8687
Var,
8788
Let,
8889
Mut,
@@ -118,6 +119,7 @@ pub enum TokenType {
118119
In, // in
119120
Out, // out
120121
Is, // is
122+
As, // as
121123
Asm,
122124
Rol,
123125
Ror,

front/parser/src/ast.rs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,10 @@ pub enum Expression {
178178
operator: Operator,
179179
expr: Box<Expression>,
180180
},
181+
Cast {
182+
expr: Box<Expression>,
183+
target_type: WaveType,
184+
},
181185
IncDec {
182186
kind: IncDecKind,
183187
target: Box<Expression>,
@@ -300,6 +304,7 @@ pub enum StatementNode {
300304

301305
#[derive(Debug, Clone, Copy, PartialEq)]
302306
pub enum Mutability {
307+
Static,
303308
Var,
304309
Let,
305310
LetMut,
@@ -354,17 +359,16 @@ impl Expression {
354359
Expression::Unary { operator, expr } => {
355360
let t = expr.get_wave_type(variables);
356361
match operator {
357-
Operator::Neg => {
358-
match &t {
359-
WaveType::Int(_) | WaveType::Uint(_) | WaveType::Float(_) => t,
360-
_ => panic!("unary '-' not allowed for type {:?}", t),
361-
}
362-
}
362+
Operator::Neg => match &t {
363+
WaveType::Int(_) | WaveType::Uint(_) | WaveType::Float(_) => t,
364+
_ => panic!("unary '-' not allowed for type {:?}", t),
365+
},
363366
Operator::Not | Operator::LogicalNot => WaveType::Bool,
364367
Operator::BitwiseNot => t,
365368
_ => panic!("unary op type inference not supported: {:?}", operator),
366369
}
367370
}
371+
Expression::Cast { target_type, .. } => target_type.clone(),
368372
_ => panic!("get_wave_type not implemented for {:?}", self),
369373
}
370374
}

front/parser/src/expr/binary.rs

Lines changed: 52 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,22 @@
99
//
1010
// SPDX-License-Identifier: MPL-2.0
1111

12-
use lexer::Token;
13-
use lexer::token::TokenType;
1412
use crate::ast::{Expression, Operator};
1513
use crate::expr::unary::parse_unary_expression;
14+
use crate::types::parse_type_from_stream;
15+
use lexer::token::TokenType;
16+
use lexer::Token;
1617

1718
pub fn parse_logical_or_expression<'a, T>(tokens: &mut std::iter::Peekable<T>) -> Option<Expression>
1819
where
1920
T: Iterator<Item = &'a Token>,
2021
{
2122
let mut left = parse_logical_and_expression(tokens)?;
2223

23-
while matches!(tokens.peek().map(|t| &t.token_type), Some(TokenType::LogicalOr)) {
24+
while matches!(
25+
tokens.peek().map(|t| &t.token_type),
26+
Some(TokenType::LogicalOr)
27+
) {
2428
tokens.next();
2529
let right = parse_logical_and_expression(tokens)?;
2630
left = Expression::BinaryExpression {
@@ -33,13 +37,18 @@ where
3337
Some(left)
3438
}
3539

36-
pub fn parse_logical_and_expression<'a, T>(tokens: &mut std::iter::Peekable<T>) -> Option<Expression>
40+
pub fn parse_logical_and_expression<'a, T>(
41+
tokens: &mut std::iter::Peekable<T>,
42+
) -> Option<Expression>
3743
where
3844
T: Iterator<Item = &'a Token>,
3945
{
4046
let mut left = parse_bitwise_or_expression(tokens)?;
4147

42-
while matches!(tokens.peek().map(|t| &t.token_type), Some(TokenType::LogicalAnd)) {
48+
while matches!(
49+
tokens.peek().map(|t| &t.token_type),
50+
Some(TokenType::LogicalAnd)
51+
) {
4352
tokens.next();
4453
let right = parse_bitwise_or_expression(tokens)?;
4554
left = Expression::BinaryExpression {
@@ -58,7 +67,10 @@ where
5867
{
5968
let mut left = parse_bitwise_xor_expression(tokens)?;
6069

61-
while matches!(tokens.peek().map(|t| &t.token_type), Some(TokenType::BitwiseOr)) {
70+
while matches!(
71+
tokens.peek().map(|t| &t.token_type),
72+
Some(TokenType::BitwiseOr)
73+
) {
6274
tokens.next();
6375
let right = parse_bitwise_xor_expression(tokens)?;
6476
left = Expression::BinaryExpression {
@@ -71,7 +83,9 @@ where
7183
Some(left)
7284
}
7385

74-
pub fn parse_bitwise_xor_expression<'a, T>(tokens: &mut std::iter::Peekable<T>) -> Option<Expression>
86+
pub fn parse_bitwise_xor_expression<'a, T>(
87+
tokens: &mut std::iter::Peekable<T>,
88+
) -> Option<Expression>
7589
where
7690
T: Iterator<Item = &'a Token>,
7791
{
@@ -90,13 +104,18 @@ where
90104
Some(left)
91105
}
92106

93-
pub fn parse_bitwise_and_expression<'a, T>(tokens: &mut std::iter::Peekable<T>) -> Option<Expression>
107+
pub fn parse_bitwise_and_expression<'a, T>(
108+
tokens: &mut std::iter::Peekable<T>,
109+
) -> Option<Expression>
94110
where
95111
T: Iterator<Item = &'a Token>,
96112
{
97113
let mut left = parse_equality_expression(tokens)?;
98114

99-
while matches!(tokens.peek().map(|t| &t.token_type), Some(TokenType::AddressOf)) {
115+
while matches!(
116+
tokens.peek().map(|t| &t.token_type),
117+
Some(TokenType::AddressOf)
118+
) {
100119
tokens.next();
101120
let right = parse_equality_expression(tokens)?;
102121
left = Expression::BinaryExpression {
@@ -208,11 +227,13 @@ where
208227
Some(left)
209228
}
210229

211-
pub fn parse_multiplicative_expression<'a, T>(tokens: &mut std::iter::Peekable<T>) -> Option<Expression>
230+
pub fn parse_multiplicative_expression<'a, T>(
231+
tokens: &mut std::iter::Peekable<T>,
232+
) -> Option<Expression>
212233
where
213234
T: Iterator<Item = &'a Token>,
214235
{
215-
let mut left = parse_unary_expression(tokens)?;
236+
let mut left = parse_cast_expression(tokens)?;
216237

217238
while let Some(token) = tokens.peek() {
218239
let op = match token.token_type {
@@ -222,7 +243,7 @@ where
222243
_ => break,
223244
};
224245
tokens.next();
225-
let right = parse_unary_expression(tokens)?;
246+
let right = parse_cast_expression(tokens)?;
226247
left = Expression::BinaryExpression {
227248
left: Box::new(left),
228249
operator: op,
@@ -231,4 +252,22 @@ where
231252
}
232253

233254
Some(left)
234-
}
255+
}
256+
257+
fn parse_cast_expression<'a, T>(tokens: &mut std::iter::Peekable<T>) -> Option<Expression>
258+
where
259+
T: Iterator<Item = &'a Token>,
260+
{
261+
let mut expr = parse_unary_expression(tokens)?;
262+
263+
while matches!(tokens.peek().map(|t| &t.token_type), Some(TokenType::As)) {
264+
tokens.next(); // consume `as`
265+
let target_type = parse_type_from_stream(tokens)?;
266+
expr = Expression::Cast {
267+
expr: Box::new(expr),
268+
target_type,
269+
};
270+
}
271+
272+
Some(expr)
273+
}

front/parser/src/import.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
// SPDX-License-Identifier: MPL-2.0
1111

1212
use crate::ast::{ASTNode};
13-
use crate::{parse, ParseError};
13+
use crate::{parse_syntax_only, ParseError};
1414
use error::error::{WaveError, WaveErrorKind};
1515
use lexer::Lexer;
1616
use std::collections::{HashMap, HashSet};
@@ -347,7 +347,7 @@ fn parse_wave_file(
347347
let mut lexer = Lexer::new_with_file(&content, abs_path.display().to_string());
348348
let tokens = lexer.tokenize()?;
349349

350-
let ast = parse(&tokens).map_err(|e| {
350+
let ast = parse_syntax_only(&tokens).map_err(|e| {
351351
let (kind, phase, code) = match &e {
352352
ParseError::Syntax(_) => (WaveErrorKind::SyntaxError(e.message().to_string()), "syntax", "E2001"),
353353
ParseError::Semantic(_) => (WaveErrorKind::InvalidStatement(e.message().to_string()), "semantic", "E3001"),

front/parser/src/parser/control.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -230,8 +230,12 @@ fn parse_for_initializer(tokens: &mut Peekable<Iter<Token>>) -> Option<ASTNode>
230230
parse_typed_for_initializer(tokens, mutability)
231231
}
232232
Some(TokenType::Const) => {
233-
tokens.next(); // consume `const`
234-
parse_typed_for_initializer(tokens, Mutability::Const)
233+
println!("Error: `const` is not allowed in local for-loop initializer");
234+
None
235+
}
236+
Some(TokenType::Static) => {
237+
println!("Error: `static` is not allowed in local for-loop initializer");
238+
None
235239
}
236240
_ if is_typed_for_initializer(tokens) => parse_typed_for_initializer(tokens, Mutability::Var),
237241
_ => {

0 commit comments

Comments
 (0)