1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
//! ASAP2/A2L ECU description parser — PROJECT/MODULE/CHARACTERISTIC/
//! MEASUREMENT/RECORD_LAYOUT/COMPU_METHOD, calibration data model.
//!
//! `a2l-parse` is the estate's **L1 substrate** for the description half
//! of the calibration stack: it turns A2L (ASAM MCD-2 MC / ASAP2) ECU
//! description files into a typed data model that pairs with `xcp-core`'s
//! memory access (UPLOAD/DOWNLOAD to the addresses found here).
//!
//! # Skip-tolerant, total-ish parsing
//!
//! The grammar is keyword-driven and block-structured (`/begin NAME …
//! /end NAME`). The parser consumes the required positional parameters of
//! the six core blocks, captures the optional keywords the model needs
//! (`ECU_ADDRESS`, `FNC_VALUES`, `COEFFS`, `COEFFS_LINEAR`,
//! `COMPU_TAB_REF`), and **skips** everything else — unknown optional
//! keywords, unknown `/begin` sub-blocks ([`AXIS_DESCR`-style vendor
//! extensions]), `IF_DATA` bodies with braces. Real-world files with
//! vendor extensions parse as long as the core blocks are intact. Malformed
//! input resolves to a typed [`A2lError`] with a 1-based line number —
//! never a panic (deny-level lints on the lib target, a cargo-fuzz target,
//! miri).
//!
//! # Example
//!
//! ```
//! use a2l_parse::{A2lProject, CharType, CompuMethod, ConversionType, DataType};
//!
//! let a2l = r#"
//! /begin PROJECT demo "Demonstration project"
//! /begin MODULE engine "V8 ECU"
//! /begin CHARACTERISTIC eng_load "Engine load" VALUE 0x720000
//! RL_VALUE 0 rack_lin 0.0 100.0
//! /end CHARACTERISTIC
//! /begin MEASUREMENT engine_speed "Engine speed" UWORD
//! engine_speed_c 0.01 0.0 0.0 8000.0
//! ECU_ADDRESS 0x320001
//! /end MEASUREMENT
//! /begin COMPU_METHOD rack_lin "Rack position" RAT_FUNC
//! "%.4f" "%" COEFFS 0 0.00390625 0 0 0 1
//! /end COMPU_METHOD
//! /end MODULE
//! /end PROJECT
//! "#;
//! let project = A2lProject::parse(a2l)?;
//!
//! assert_eq!(project.project, "demo");
//! assert_eq!(project.modules.len(), 1);
//!
//! let module = project.module_by_name("engine").unwrap();
//! let kfile = module.characteristic_by_name("eng_load").unwrap();
//! assert_eq!(kfile.r#type, CharType::Value);
//! assert_eq!(kfile.address, 0x720000);
//! assert_eq!(kfile.deposit, "RL_VALUE");
//! assert_eq!(kfile.lower_limit, 0.0);
//! assert_eq!(kfile.upper_limit, 100.0);
//!
//! let meas = module.measurement_by_name("engine_speed").unwrap();
//! assert_eq!(meas.datatype, DataType::Uword);
//! assert_eq!(meas.address, 0x320001);
//!
//! // Raw → physical conversion of the RAT_FUNC method:
//! let cm = module.compu_method_by_name("rack_lin").unwrap();
//! assert_eq!(cm.conversion_type, ConversionType::RatFunc);
//! let lin = cm.to_linear().unwrap();
//! assert_eq!(lin.apply(256.0), 1.0);
//! # Ok::<(), a2l_parse::A2lError>(())
//! ```
//!
//! # `no_std`
//!
//! The crate is `no_std` + `alloc` with **zero runtime dependencies** —
//! the scanner, parser, and error type are hand-rolled over `core` +
//! `alloc`.
extern crate alloc;
pub use A2lError;
pub use ;