Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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:

  • parc owns preprocessing, parsing, extraction, and source diagnostics
  • linc owns link and binary evidence
  • gerc owns 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:

FlavorMeaning
StdC11Strict C11
GnuC11C11 plus GNU syntax such as typeof, attributes, statement expressions, and GNU asm
ClangC11C11 plus Clang-oriented extensions such as availability attributes

For file-based parsing, Config::default() selects:

  • clang -E on macOS
  • gcc -E on 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);
}

Architectural boundary

parc is the source frontend.

It owns:

  • preprocessing
  • parsing
  • source extraction
  • source diagnostics
  • the parc::ir::SourcePackage artifact

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.