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

Common Workflows

Most confusion with PARC comes from choosing the wrong entry point. This chapter maps common tasks to the right API.

Read the workflows in this order:

  1. prefer source/frontend workflows that stay inside parc
  2. serialize SourcePackage when another tool needs the result
  3. keep any cross-package translation in tests, examples, or external harnesses

Workflow selection

SituationAPI
Turn headers into SourcePackagescan::scan_headers
Parse a .c or .h file with includes and macrosdriver::parse
Parse already-preprocessed text from memorydriver::parse_preprocessed
Parse one expression, declaration, statement, or translation unit stringparse::*
Walk an AST you already parsedvisit
Print an AST for debuggingprint::Printer

Scan headers into source IR

Use this when your real target is the PARC source contract rather than the raw syntax tree.

#![allow(unused)]
fn main() {
use parc::scan::{scan_headers, ScanConfig};

let result = scan_headers(&ScanConfig::new().entry_header("demo.h")).unwrap();
println!("diagnostics: {}", result.package.diagnostics.len());
}

This is the best fit for downstream toolchains that want declarations, provenance, macros, and diagnostics in one package.

Parse a real file

Use this when your source depends on #include, #define, or compiler predefined macros.

#![allow(unused)]
fn main() {
use parc::driver::{parse, Config};

let config = Config::default();
let parsed = parse(&config, "src/main.c")?;
}

This gives you:

  • parsed.source: the preprocessed source text
  • parsed.unit: the AST root

Parse preprocessed text

Use this when another tool already ran preprocessing and you only want PARC to parse.

#![allow(unused)]
fn main() {
use parc::driver::{parse_preprocessed, Config};

let config = Config::default();
let source = r#"
1 "generated.i"
typedef int count_t;
count_t answer(void) { return 42; }
"#
.to_string();

let parsed = parse_preprocessed(&config, source)?;
}

This is useful for:

  • snapshot-based tests
  • integration with custom build systems
  • reproducing parse bugs from stored .i files

Parse a fragment

Use parc::parse when you are not dealing with a whole file.

#![allow(unused)]
fn main() {
use parc::driver::Flavor;
use parc::parse;

let expr = parse::expression("ptr->len + 1", Flavor::GnuC11)?;
let decl = parse::declaration("unsigned long flags;", Flavor::StdC11)?;
let stmt = parse::statement("if (ok) return 1;", Flavor::StdC11)?;
}

This is the right choice for:

  • unit tests
  • parser experiments
  • editor tooling for partial snippets

Build an analyzer

The normal analyzer flow is:

  1. Parse with driver or parse
  2. Traverse with visit
  3. Use span and loc for diagnostics

Example outline:

#![allow(unused)]
fn main() {
use parc::driver::{parse, Config};
use parc::visit::{self, Visit};
use parc::{ast, span};

struct FunctionCounter {
    count: usize,
}

impl<'ast> Visit<'ast> for FunctionCounter {
    fn visit_function_definition(
        &mut self,
        node: &'ast ast::FunctionDefinition,
        span: &'ast span::Span,
    ) {
        self.count += 1;
        visit::visit_function_definition(self, node, span);
    }
}

let parsed = parse(&Config::default(), "src/main.c")?;
let mut counter = FunctionCounter { count: 0 };
counter.visit_translation_unit(&parsed.unit);
println!("functions: {}", counter.count);
Ok::<(), parc::driver::Error>(())
}

Debug the parse tree

Use the printer when you need a human-readable structural dump:

#![allow(unused)]
fn main() {
use parc::driver::{parse, Config};
use parc::print::Printer;
use parc::visit::Visit;

let parsed = parse(&Config::default(), "src/main.c")?;

let mut out = String::new();
Printer::new(&mut out).visit_translation_unit(&parsed.unit);
println!("{}", out);
Ok::<(), parc::driver::Error>(())
}

Rule of thumb

  • If you want SourcePackage, start with scan.
  • If preprocessing matters and you still want the AST, start with driver.
  • If you already have plain text in memory, start with parse.
  • If you need diagnostics tied back to original files, keep the preprocessed source string.
  • If another crate needs PARC output, stop at SourcePackage and translate it outside parc/src/**.