Skip to content

Commit f7455cf

Browse files
committed
semantic: implement GCC and Clang specific interpreters
1 parent a49dbdd commit f7455cf

File tree

10 files changed

+3552
-50
lines changed

10 files changed

+3552
-50
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ directories = "6.0"
3232
shell-words = "1.1"
3333
tempfile = { version = "3.19", default-features = false }
3434
signal-hook = { version = "0.3", default-features = false }
35+
regex = "1.0"
3536
libc = "0.2"
3637
which = "8.0"
3738
cc = "1.2"

bear/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ shell-words.workspace = true
3333
tempfile.workspace = true
3434
signal-hook.workspace = true
3535
crossbeam-channel.workspace = true
36+
regex.workspace = true
3637

3738
[dev-dependencies]
3839
tempfile.workspace = true
Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
// SPDX-License-Identifier: GPL-3.0-or-later
2+
3+
//! Specialized Arguments implementations for different compiler argument types.
4+
//!
5+
//! This module provides concrete implementations of the [`Arguments`] trait
6+
//! for various types of compiler arguments, enabling more sophisticated
7+
//! argument parsing than the basic [`BasicArguments`] implementation.
8+
9+
use crate::semantic::{ArgumentKind, Arguments};
10+
use std::borrow::Cow;
11+
use std::path::{Path, PathBuf};
12+
13+
/// Represents a generic argument that holds an ArgumentKind and a vector of strings.
14+
///
15+
/// This handles all argument types except source and output files:
16+
/// - Simple flags: `-c`, `-Wall`
17+
/// - Flags with values: `-I /usr/include`, `-D MACRO=value`
18+
/// - Combined flags: `-Ipath`, `-DMACRO=value`
19+
/// - Compiler executable names
20+
/// - Response files: `@file`
21+
/// - Any other arguments
22+
#[derive(Debug, Clone, PartialEq)]
23+
pub struct OtherArguments {
24+
/// The argument strings (e.g., ["-I", "/usr/include"] or ["-Wall"])
25+
arguments: Vec<String>,
26+
/// The semantic meaning of this argument
27+
kind: ArgumentKind,
28+
}
29+
30+
impl OtherArguments {
31+
/// Creates a new argument with the given strings and kind.
32+
pub fn new(arguments: Vec<String>, kind: ArgumentKind) -> Self {
33+
Self { arguments, kind }
34+
}
35+
}
36+
37+
impl Arguments for OtherArguments {
38+
fn kind(&self) -> ArgumentKind {
39+
self.kind.clone()
40+
}
41+
42+
fn as_arguments(&self, _path_updater: &dyn Fn(&Path) -> Cow<Path>) -> Vec<String> {
43+
self.arguments.clone()
44+
}
45+
46+
fn as_file(&self, _path_updater: &dyn Fn(&Path) -> Cow<Path>) -> Option<PathBuf> {
47+
// Other arguments don't represent files directly
48+
None
49+
}
50+
}
51+
52+
/// Represents a source file argument.
53+
#[derive(Debug, Clone, PartialEq)]
54+
pub struct SourceArgument {
55+
/// Path to the source file
56+
path: String,
57+
}
58+
59+
impl SourceArgument {
60+
/// Creates a new source file argument.
61+
pub fn new(path: String) -> Self {
62+
Self { path }
63+
}
64+
}
65+
66+
impl Arguments for SourceArgument {
67+
fn kind(&self) -> ArgumentKind {
68+
ArgumentKind::Source
69+
}
70+
71+
fn as_arguments(&self, path_updater: &dyn Fn(&Path) -> Cow<Path>) -> Vec<String> {
72+
let path = Path::new(&self.path);
73+
let updated_path = path_updater(path);
74+
vec![updated_path.to_string_lossy().to_string()]
75+
}
76+
77+
fn as_file(&self, path_updater: &dyn Fn(&Path) -> Cow<Path>) -> Option<PathBuf> {
78+
let path = Path::new(&self.path);
79+
let updated_path = path_updater(path);
80+
Some(updated_path.to_path_buf())
81+
}
82+
}
83+
84+
/// Represents an output file argument.
85+
#[derive(Debug, Clone, PartialEq)]
86+
pub struct OutputArgument {
87+
/// The output flag (usually "-o")
88+
flag: String,
89+
/// Path to the output file
90+
path: String,
91+
}
92+
93+
impl OutputArgument {
94+
/// Creates a new output argument.
95+
pub fn new(flag: String, path: String) -> Self {
96+
Self { flag, path }
97+
}
98+
}
99+
100+
impl Arguments for OutputArgument {
101+
fn kind(&self) -> ArgumentKind {
102+
ArgumentKind::Output
103+
}
104+
105+
fn as_arguments(&self, path_updater: &dyn Fn(&Path) -> Cow<Path>) -> Vec<String> {
106+
let path = Path::new(&self.path);
107+
let updated_path = path_updater(path);
108+
vec![
109+
self.flag.clone(),
110+
updated_path.to_string_lossy().to_string(),
111+
]
112+
}
113+
114+
fn as_file(&self, path_updater: &dyn Fn(&Path) -> Cow<Path>) -> Option<PathBuf> {
115+
let path = Path::new(&self.path);
116+
let updated_path = path_updater(path);
117+
Some(updated_path.to_path_buf())
118+
}
119+
}
120+
121+
#[cfg(test)]
122+
mod tests {
123+
use super::*;
124+
use crate::semantic::CompilerPass;
125+
126+
#[test]
127+
fn test_other_arguments_new_with_simple_flag() {
128+
let arg = OtherArguments::new(
129+
vec!["-c".to_string()],
130+
ArgumentKind::Other(Some(CompilerPass::Compiling)),
131+
);
132+
133+
assert_eq!(
134+
arg.kind(),
135+
ArgumentKind::Other(Some(CompilerPass::Compiling))
136+
);
137+
assert_eq!(arg.as_arguments(&|p| Cow::Borrowed(p)), vec!["-c"]);
138+
assert_eq!(arg.as_file(&|p| Cow::Borrowed(p)), None);
139+
}
140+
141+
#[test]
142+
fn test_other_arguments_new_with_flag_and_value() {
143+
let arg = OtherArguments::new(
144+
vec!["-I".to_string(), "/usr/include".to_string()],
145+
ArgumentKind::Other(Some(CompilerPass::Preprocessing)),
146+
);
147+
148+
assert_eq!(
149+
arg.kind(),
150+
ArgumentKind::Other(Some(CompilerPass::Preprocessing))
151+
);
152+
assert_eq!(
153+
arg.as_arguments(&|p| Cow::Borrowed(p)),
154+
vec!["-I", "/usr/include"]
155+
);
156+
assert_eq!(arg.as_file(&|p| Cow::Borrowed(p)), None);
157+
}
158+
159+
#[test]
160+
fn test_other_arguments_new_with_combined_flag() {
161+
let arg = OtherArguments::new(
162+
vec!["-I/usr/include".to_string()],
163+
ArgumentKind::Other(Some(CompilerPass::Preprocessing)),
164+
);
165+
166+
assert_eq!(
167+
arg.kind(),
168+
ArgumentKind::Other(Some(CompilerPass::Preprocessing))
169+
);
170+
assert_eq!(
171+
arg.as_arguments(&|p| Cow::Borrowed(p)),
172+
vec!["-I/usr/include"]
173+
);
174+
assert_eq!(arg.as_file(&|p| Cow::Borrowed(p)), None);
175+
}
176+
177+
#[test]
178+
fn test_other_arguments_new_with_compiler() {
179+
let arg = OtherArguments::new(vec!["gcc".to_string()], ArgumentKind::Compiler);
180+
181+
assert_eq!(arg.kind(), ArgumentKind::Compiler);
182+
assert_eq!(arg.as_arguments(&|p| Cow::Borrowed(p)), vec!["gcc"]);
183+
assert_eq!(arg.as_file(&|p| Cow::Borrowed(p)), None);
184+
}
185+
186+
#[test]
187+
fn test_other_arguments_new_with_response_file() {
188+
let arg = OtherArguments::new(vec!["@response.txt".to_string()], ArgumentKind::Other(None));
189+
190+
assert_eq!(arg.kind(), ArgumentKind::Other(None));
191+
assert_eq!(
192+
arg.as_arguments(&|p| Cow::Borrowed(p)),
193+
vec!["@response.txt"]
194+
);
195+
assert_eq!(arg.as_file(&|p| Cow::Borrowed(p)), None);
196+
}
197+
198+
#[test]
199+
fn test_source_argument() {
200+
let arg = SourceArgument::new("main.c".to_string());
201+
202+
assert_eq!(arg.kind(), ArgumentKind::Source);
203+
assert_eq!(arg.as_arguments(&|p| Cow::Borrowed(p)), vec!["main.c"]);
204+
assert_eq!(
205+
arg.as_file(&|p| Cow::Borrowed(p)),
206+
Some(PathBuf::from("main.c"))
207+
);
208+
}
209+
210+
#[test]
211+
fn test_output_argument() {
212+
let arg = OutputArgument::new("-o".to_string(), "main.o".to_string());
213+
214+
assert_eq!(arg.kind(), ArgumentKind::Output);
215+
assert_eq!(
216+
arg.as_arguments(&|p| Cow::Borrowed(p)),
217+
vec!["-o", "main.o"]
218+
);
219+
assert_eq!(
220+
arg.as_file(&|p| Cow::Borrowed(p)),
221+
Some(PathBuf::from("main.o"))
222+
);
223+
}
224+
225+
#[test]
226+
fn test_path_updater_functionality() {
227+
let arg = SourceArgument::new("src/main.c".to_string());
228+
229+
// Test with identity path updater (no change)
230+
assert_eq!(
231+
arg.as_arguments(&|p| std::borrow::Cow::Borrowed(p)),
232+
vec!["src/main.c"]
233+
);
234+
assert_eq!(
235+
arg.as_file(&|p| std::borrow::Cow::Borrowed(p)),
236+
Some(PathBuf::from("src/main.c"))
237+
);
238+
}
239+
240+
#[test]
241+
fn test_other_arguments_new() {
242+
let arg = OtherArguments::new(
243+
vec!["-D".to_string(), "MACRO=value".to_string()],
244+
ArgumentKind::Other(Some(CompilerPass::Preprocessing)),
245+
);
246+
247+
assert_eq!(
248+
arg.kind(),
249+
ArgumentKind::Other(Some(CompilerPass::Preprocessing))
250+
);
251+
assert_eq!(
252+
arg.as_arguments(&|p| Cow::Borrowed(p)),
253+
vec!["-D", "MACRO=value"]
254+
);
255+
assert_eq!(arg.as_file(&|p| Cow::Borrowed(p)), None);
256+
}
257+
}

0 commit comments

Comments
 (0)