Getting Started
This chapter is the shortest path from real source or headers to something that
PARC actually produces today: either a parsed AST or a SourcePackage.
Read parc as the source frontend of the toolchain:
parcowns preprocessing, parsing, extraction, and source diagnosticslincowns link and binary evidencegercowns Rust lowering and emitted build output
The boundary rule is strict: parc/src/** must not depend on linc or gerc,
and any cross-package translation belongs only in tests, examples, or external
harnesses.
Add the crate
[dependencies]
parc = { path = "../parc" }
Pick the right API first
Use parc::driver when you have a file on disk and want PARC to run a system
preprocessor first.
use parc::driver::{parse, Config};
fn main() -> Result<(), parc::driver::Error> {
let config = Config::default();
let parsed = parse(&config, "src/tests/files/minimal.c")?;
println!("preprocessed bytes: {}", parsed.source.len());
println!("top-level items: {}", parsed.unit.0.len());
Ok(())
}
Use parc::parse when you already have source text in memory and want to parse
a fragment directly.
use parc::driver::Flavor;
use parc::parse;
fn main() {
let expr = parse::expression("a + b * 2", Flavor::StdC11).unwrap();
println!("{:#?}", expr);
}
Choose a language flavor
PARC supports three parser modes:
| Flavor | Meaning |
|---|---|
StdC11 | Strict C11 |
GnuC11 | C11 plus GNU syntax such as typeof, attributes, statement expressions, and GNU asm |
ClangC11 | C11 plus Clang-oriented extensions such as availability attributes |
For file-based parsing, Config::default() selects:
clang -Eon macOSgcc -Eon other targets
You can also select explicitly:
#![allow(unused)]
fn main() {
use parc::driver::Config;
let gnu = Config::with_gcc();
let clang = Config::with_clang();
}
First useful parse example
This example parses a translation unit through the normal driver path:
use parc::driver::{parse, Config};
fn main() -> Result<(), parc::driver::Error> {
let parsed = parse(&Config::default(), "src/tests/files/minimal.c")?;
for (i, item) in parsed.unit.0.iter().enumerate() {
println!("item #{i}: {:?}", item.node);
}
Ok(())
}
First useful scan example
If what you really want is source IR rather than a raw AST, start with
parc::scan:
#![allow(unused)]
fn main() {
use parc::scan::{scan_headers, ScanConfig};
let config = ScanConfig::new().entry_header("demo.h");
let result = scan_headers(&config).unwrap();
println!("items: {}", result.package.items.len());
}
This is the closest thing PARC has to a “frontend product” API.
First fragment example
If you only need one declaration or statement, the direct parser API is faster to wire in:
use parc::driver::Flavor;
use parc::parse;
fn main() {
let decl = parse::declaration("static const int answer = 42;", Flavor::StdC11).unwrap();
let stmt = parse::statement("return answer;", Flavor::StdC11).unwrap();
println!("{:#?}", decl);
println!("{:#?}", stmt);
}
What to read next
- Common Workflows for choosing between
scan,driver,parse_preprocessed, andparse - Driver API for preprocessing and file-based parsing
- Header Scanning for source-contract-first workflows
- Parser API for fragment parsing
Architectural boundary
parc is the source frontend.
It owns:
- preprocessing
- parsing
- source extraction
- source diagnostics
- the
parc::ir::SourcePackageartifact
It does not own:
- symbol inventory
- binary validation
- link planning
- Rust code generation
In this repository, cross-package composition should not live in parc library
code. linc and gerc should consume parc output only from tests, examples,
or external harnesses.