1use nom::{
7 branch::alt,
8 bytes::complete::{tag, take_till, take_while, take_while1},
9 character::complete::{alpha1, char, multispace1, not_line_ending},
10 combinator::{map, opt, peek, recognize, value},
11 multi::{many0, separated_list1},
12 number::complete::double,
13 sequence::{delimited, pair, preceded, tuple},
14 IResult,
15};
16
17use crate::compiler::components::*;
18
19type BoxComp = Box<dyn crate::compiler::Component>;
21
22#[derive(Debug, Clone, Copy, PartialEq, Default)]
28pub enum RectifierType {
29 Tube,
32 #[default]
35 SolidState,
36}
37
38#[derive(Debug, Clone, PartialEq)]
47pub struct SupplyConfig {
48 pub voltage: f64,
50 pub impedance: Option<f64>,
53 pub filter_cap: Option<f64>,
56 pub rectifier: RectifierType,
58}
59
60impl SupplyConfig {
61 pub fn voltage_only(voltage: f64) -> Self {
63 Self {
64 voltage,
65 impedance: None,
66 filter_cap: None,
67 rectifier: RectifierType::default(),
68 }
69 }
70
71 pub fn has_sag(&self) -> bool {
73 self.impedance.is_some()
74 }
75}
76
77#[derive(Debug, Clone, PartialEq)]
82pub struct NamedSupply {
83 pub name: String,
85 pub config: SupplyConfig,
87}
88
89impl NamedSupply {
90 pub fn new(name: impl Into<String>, voltage: f64) -> Self {
92 Self {
93 name: name.into(),
94 config: SupplyConfig::voltage_only(voltage),
95 }
96 }
97
98 pub fn with_config(name: impl Into<String>, config: SupplyConfig) -> Self {
100 Self {
101 name: name.into(),
102 config,
103 }
104 }
105}
106
107#[derive(Debug, Clone, PartialEq)]
109pub enum SidechainTarget {
110 PushPullGridBias,
113}
114
115#[derive(Debug, Clone, PartialEq)]
129pub struct SidechainInfo {
130 pub tap_node: String,
132 pub cv_node: String,
134 pub target: SidechainTarget,
136}
137
138#[derive(Debug, Clone, PartialEq)]
150pub enum InitState {
151 Named(String),
153 Explicit { vbe: f64, vce: f64 },
161 NodeVoltage { v: f64 },
169}
170
171#[derive(Debug, Clone, PartialEq)]
180pub struct InitHint {
181 pub device_label: String,
183 pub state: InitState,
185}
186
187#[derive(Debug, Clone, PartialEq)]
189pub struct PedalDef {
190 pub name: String,
191 pub subtitle: Option<String>,
193 pub supplies: Vec<NamedSupply>,
197 pub components: Vec<ComponentDef>,
198 pub nets: Vec<NetDef>,
199 pub controls: Vec<ControlDef>,
200 pub trims: Vec<ControlDef>,
202 pub monitors: Vec<MonitorDef>,
204 pub sidechains: Vec<SidechainInfo>,
207 pub mirrors: hashbrown::HashMap<String, String>,
211 pub calibrate: bool,
213 pub subcircuits: Vec<SubcircuitDef>,
216 pub ports: Vec<PortDef>,
220 pub init_hints: Vec<InitHint>,
226 pub uses: Vec<UseInstance>,
232}
233
234#[derive(Debug, Clone, PartialEq)]
246pub struct UseInstance {
247 pub id: String,
249 pub path: String,
251 pub overrides: Vec<(String, f64)>,
255}
256
257#[derive(Debug, Clone, PartialEq)]
267pub struct PortDef {
268 pub name: String,
269 pub direction: pedalkernel_rt::PortDirection,
270 pub impedance: Option<f64>,
274}
275
276#[derive(Debug, Clone, PartialEq)]
280pub struct SubcircuitDef {
281 pub name: String,
283 pub rate: Option<u32>,
285 pub components: Vec<ComponentDef>,
287 pub nets: Vec<NetDef>,
290 pub controls: Vec<ControlDef>,
292 pub trims: Vec<ControlDef>,
294 pub monitors: Vec<MonitorDef>,
296 pub mirrors: hashbrown::HashMap<String, String>,
298}
299
300impl PedalDef {
301 pub fn has_fx_loop(&self) -> bool {
303 let has_send = self.nets.iter().any(|n| {
304 n.from == Pin::Reserved("fx_send".into())
305 || n.to.contains(&Pin::Reserved("fx_send".into()))
306 });
307 let has_return = self.nets.iter().any(|n| {
308 n.from == Pin::Reserved("fx_return".into())
309 || n.to.contains(&Pin::Reserved("fx_return".into()))
310 });
311 has_send && has_return
312 }
313
314 pub fn primary_supply_voltage(&self) -> f64 {
317 self.supplies
318 .first()
319 .map(|s| s.config.voltage)
320 .unwrap_or(9.0)
321 }
322
323 pub fn get_supply(&self, name: &str) -> Option<&NamedSupply> {
325 self.supplies.iter().find(|s| s.name == name)
326 }
327
328 pub fn supply_names(&self) -> Vec<&str> {
330 self.supplies.iter().map(|s| s.name.as_str()).collect()
331 }
332
333 pub fn is_supply_rail(&self, name: &str) -> bool {
335 self.supplies.iter().any(|s| s.name == name)
336 }
337}
338
339#[derive(Debug, Clone)]
341pub struct ComponentDef {
342 pub id: String,
343 pub kind: Box<dyn crate::compiler::Component>,
344}
345
346impl PartialEq for ComponentDef {
347 fn eq(&self, other: &Self) -> bool {
348 self.id == other.id && self.kind.dyn_eq(other.kind.as_ref())
349 }
350}
351
352pub use crate::pot_taper::PotTaper;
353
354#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
358pub enum OpAmpType {
359 #[default]
361 Generic,
362 Tl071,
366 Tl072,
369 Jrc4558,
372 Lm308,
375 Lm741,
378 Ne5532,
381 Ca3080,
384 Rc4558,
386 Tl082,
388 Op07,
390}
391
392impl OpAmpType {
393 pub fn slew_rate(&self) -> f64 {
395 match self {
396 OpAmpType::Generic | OpAmpType::Tl071 | OpAmpType::Tl072 | OpAmpType::Tl082 => 13.0,
397 OpAmpType::Jrc4558 | OpAmpType::Rc4558 => 1.7,
398 OpAmpType::Lm308 => 0.3,
399 OpAmpType::Lm741 => 0.5,
400 OpAmpType::Ne5532 => 9.0,
401 OpAmpType::Ca3080 => 50.0, OpAmpType::Op07 => 0.3,
403 }
404 }
405
406 pub fn gain_bandwidth(&self) -> f64 {
408 match self {
409 OpAmpType::Generic | OpAmpType::Tl071 | OpAmpType::Tl072 | OpAmpType::Tl082 => 3e6,
410 OpAmpType::Jrc4558 | OpAmpType::Rc4558 => 3e6,
411 OpAmpType::Lm308 => 1e6,
412 OpAmpType::Lm741 => 1e6,
413 OpAmpType::Ne5532 => 10e6,
414 OpAmpType::Ca3080 => 2e6, OpAmpType::Op07 => 0.6e6,
416 }
417 }
418
419 pub fn supply_max(&self) -> f64 {
421 match self {
422 OpAmpType::Generic | OpAmpType::Tl071 | OpAmpType::Tl072 | OpAmpType::Tl082 => 36.0,
423 OpAmpType::Jrc4558 | OpAmpType::Rc4558 => 36.0,
424 OpAmpType::Lm308 => 36.0,
425 OpAmpType::Lm741 => 36.0,
426 OpAmpType::Ne5532 => 44.0,
427 OpAmpType::Ca3080 => 36.0,
428 OpAmpType::Op07 => 44.0,
429 }
430 }
431
432 pub fn is_ota(&self) -> bool {
435 matches!(self, OpAmpType::Ca3080)
436 }
437}
438
439pub const DEFAULT_NPN_MODEL: &str = "GENERIC_NPN";
441pub const DEFAULT_PNP_MODEL: &str = "GENERIC_PNP";
443
444#[derive(Debug, Clone, Copy, PartialEq, Eq)]
445pub enum DiodeType {
446 Silicon,
447 Germanium,
448 Led,
449 Schottky,
450}
451
452#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
458pub enum CapType {
459 #[default]
463 Film,
464 Electrolytic,
469 Ceramic,
474 Tantalum,
478}
479
480#[derive(Debug, Clone, Copy, PartialEq)]
484pub struct CapConfig {
485 pub value: f64,
487 pub cap_type: CapType,
489 pub leakage: Option<f64>,
492 pub da: Option<f64>,
496}
497
498impl CapConfig {
499 pub fn new(value: f64) -> Self {
500 Self {
501 value,
502 cap_type: CapType::Film,
503 leakage: None,
504 da: None,
505 }
506 }
507
508 pub fn with_type(mut self, cap_type: CapType) -> Self {
509 self.cap_type = cap_type;
510 self
511 }
512
513 pub fn with_leakage(mut self, leakage: f64) -> Self {
514 self.leakage = Some(leakage);
515 self
516 }
517
518 pub fn with_da(mut self, da: f64) -> Self {
519 self.da = Some(da);
520 self
521 }
522}
523
524#[derive(Debug, Clone, Copy, PartialEq, Eq)]
525pub enum PhotocouplerType {
526 Vtl5c3,
527 Vtl5c1,
528 Nsl32,
529 T4b,
532}
533
534#[derive(Debug, Clone, Copy, PartialEq, Eq)]
536pub enum LfoWaveformDsl {
537 Sine,
538 Triangle,
539 Square,
540 SawUp,
541 SawDown,
542 SampleAndHold,
543}
544
545#[derive(Debug, Clone, Copy, PartialEq, Eq)]
547pub enum VcoWaveformDsl {
548 Saw,
549 Triangle,
550 Pulse,
551}
552
553#[derive(Debug, Clone, Copy, PartialEq, Eq)]
558pub enum MosfetType {
559 N2n7000,
561 Irf520,
563 Bs250,
565 Irf9520,
567}
568
569#[derive(Debug, Clone, Copy, PartialEq, Eq)]
571pub enum BbdType {
572 Mn3207,
574 Mn3007,
576 Mn3005,
578}
579
580#[derive(Debug, Clone, Copy, PartialEq, Eq)]
582pub enum SpringTankType {
583 Type4,
585 Type8,
587 Type9,
589 Re201Tank,
591}
592
593#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
599pub enum NeonType {
600 #[default]
603 Ne2,
604 Ne51,
607 Ne83,
610}
611
612impl NeonType {
613 pub fn striking_voltage(&self) -> f64 {
615 match self {
616 NeonType::Ne2 => 90.0,
617 NeonType::Ne51 => 95.0,
618 NeonType::Ne83 => 65.0,
619 }
620 }
621
622 pub fn maintaining_voltage(&self) -> f64 {
624 match self {
625 NeonType::Ne2 => 60.0,
626 NeonType::Ne51 => 65.0,
627 NeonType::Ne83 => 50.0,
628 }
629 }
630
631 pub fn typical_current(&self) -> f64 {
633 match self {
634 NeonType::Ne2 => 0.5e-3, NeonType::Ne51 => 1.0e-3, NeonType::Ne83 => 0.3e-3, }
638 }
639
640 pub fn suitable_for_opto(&self) -> bool {
642 true
644 }
645}
646
647#[derive(Debug, Clone, Copy, PartialEq, Eq)]
651pub enum VcoType {
652 Cem3340,
654 As3340,
656 V3340,
658}
659
660#[derive(Debug, Clone, Copy, PartialEq, Eq)]
662pub enum VcfType {
663 Cem3320,
665 As3320,
667}
668
669#[derive(Debug, Clone, Copy, PartialEq, Eq)]
671pub enum VcaType {
672 Ssm2164,
674 V2164,
676}
677
678#[derive(Debug, Clone, Copy, PartialEq, Eq)]
680pub enum ComparatorType {
681 Lm311,
683 Lm393,
685}
686
687#[derive(Debug, Clone, Copy, PartialEq, Eq)]
689pub enum AnalogSwitchType {
690 Cd4066,
692 Dg411,
694}
695
696#[derive(Debug, Clone, Copy, PartialEq, Eq)]
698pub enum MatchedTransistorType {
699 Ssm2210,
701 Ca3046,
703 Lm394,
705 That340,
707}
708
709#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
715pub enum WindingType {
716 #[default]
718 Standard,
719 CenterTap,
721 PushPull,
723}
724
725#[derive(Debug, Clone, PartialEq)]
733pub struct TransformerConfig {
734 pub model: Option<String>,
737 pub turns_ratio: f64,
739 pub primary_inductance: f64,
741 pub primary_type: WindingType,
743 pub secondary_type: WindingType,
745 pub primary_dcr: f64,
747 pub secondary_dcr: f64,
749 pub capacitance: f64,
751 pub coupling: f64,
753 pub primary_leakage: Option<f64>,
755 pub secondary_leakage: Option<f64>,
757 pub magnetizing_inductance: Option<f64>,
759 pub core_loss_resistance: Option<f64>,
761 pub core_primary_turns: Option<f64>,
763 pub core_area: Option<f64>,
765 pub core_path_length: Option<f64>,
767 pub core_gap: Option<f64>,
769 pub dc_bias_current: Option<f64>,
771 pub ja_ms: Option<f64>,
773 pub ja_a: Option<f64>,
775 pub ja_alpha: Option<f64>,
777 pub ja_k: Option<f64>,
779 pub ja_c: Option<f64>,
781 pub tertiary_turns_ratio: Option<f64>,
785}
786
787impl Default for TransformerConfig {
788 fn default() -> Self {
789 Self {
790 turns_ratio: 1.0,
791 primary_inductance: 1.0,
792 model: None,
793 primary_type: WindingType::Standard,
794 secondary_type: WindingType::Standard,
795 primary_dcr: 0.0,
796 secondary_dcr: 0.0,
797 capacitance: 0.0,
798 coupling: 0.99,
799 primary_leakage: None,
800 secondary_leakage: None,
801 magnetizing_inductance: None,
802 core_loss_resistance: None,
803 core_primary_turns: None,
804 core_area: None,
805 core_path_length: None,
806 core_gap: None,
807 dc_bias_current: None,
808 ja_ms: None,
809 ja_a: None,
810 ja_alpha: None,
811 ja_k: None,
812 ja_c: None,
813 tertiary_turns_ratio: None,
814 }
815 }
816}
817
818impl TransformerConfig {
819 pub fn new(turns_ratio: f64, primary_inductance: f64) -> Self {
821 Self {
822 turns_ratio,
823 primary_inductance,
824 ..Default::default()
825 }
826 }
827
828 pub fn with_model(turns_ratio: f64, model: String) -> Self {
830 Self {
831 turns_ratio,
832 primary_inductance: 0.0,
833 coupling: 0.0,
834 model: Some(model),
835 ..Default::default()
836 }
837 }
838
839 pub fn with_center_tap(mut self) -> Self {
841 self.secondary_type = WindingType::CenterTap;
842 self
843 }
844
845 pub fn with_push_pull_primary(mut self) -> Self {
847 self.primary_type = WindingType::PushPull;
848 self
849 }
850
851 pub fn with_dcr(mut self, primary: f64, secondary: f64) -> Self {
853 self.primary_dcr = primary;
854 self.secondary_dcr = secondary;
855 self
856 }
857
858 pub fn with_capacitance(mut self, cap: f64) -> Self {
860 self.capacitance = cap;
861 self
862 }
863
864 pub fn with_tertiary(mut self, tertiary_turns_ratio: f64) -> Self {
866 self.tertiary_turns_ratio = Some(tertiary_turns_ratio);
867 self
868 }
869
870 pub fn has_tertiary(&self) -> bool {
872 self.tertiary_turns_ratio.is_some()
873 }
874}
875
876#[derive(Debug, Clone, PartialEq)]
892pub struct TapeHeadConfig {
893 pub model: Option<String>,
895 pub ja_ms: f64,
897 pub ja_a: f64,
899 pub ja_alpha: f64,
901 pub ja_k: f64,
903 pub ja_c: f64,
905 pub kv: f64,
907 pub isat: f64,
909 pub gp: f64,
911 pub h_bias: f64,
914 pub rp: f64,
916}
917
918impl Default for TapeHeadConfig {
919 fn default() -> Self {
920 Self::studio_head()
921 }
922}
923
924impl TapeHeadConfig {
925 pub fn studio_head() -> Self {
931 Self {
932 model: Some("STUDIO-HEAD".to_string()),
933 ja_ms: 3.5e5,
934 ja_a: 1500.0,
935 ja_alpha: 1.6e-3,
936 ja_k: 30.0,
937 ja_c: 0.65,
938 kv: 8000.0,
939 isat: 2.0e-4,
940 gp: 8.0e-5,
941 h_bias: 400.0,
942 rp: 1_000.0,
943 }
944 }
945
946 pub fn with_model(model: String) -> Self {
947 Self {
948 model: Some(model),
949 ..Self::studio_head()
950 }
951 }
952}
953
954#[derive(Debug, Clone, PartialEq)]
956pub struct NetDef {
957 pub from: Pin,
958 pub to: Vec<Pin>,
959}
960
961#[derive(Debug, Clone, PartialEq, Eq, Hash)]
964pub enum Pin {
965 Reserved(String),
966 ComponentPin {
967 component: String,
968 pin: String,
969 },
970 Fork {
974 switch: String,
976 destinations: Vec<Pin>,
978 },
979 SubcircuitPort {
982 subcircuit: String,
983 port: String,
984 },
985}
986
987#[derive(Debug, Clone, PartialEq)]
989pub struct ControlDef {
990 pub component: String,
991 pub property: String,
992 pub label: String,
993 pub range: (f64, f64),
994 pub default: f64,
995}
996
997#[derive(Debug, Clone, PartialEq)]
1012pub struct MonitorDef {
1013 pub component: String,
1015 pub property: String,
1017 pub label: String,
1019 pub meter_type: MeterType,
1021}
1022
1023#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
1025pub enum MeterType {
1026 #[default]
1028 Vu,
1029 Ppm,
1031 Peak,
1033 GainReduction,
1035 TubeGlow,
1037 SupplySag,
1039}
1040
1041fn ws_comments(input: &str) -> IResult<&str, ()> {
1047 let (input, _) = many0(alt((
1048 value((), multispace1),
1049 value((), pair(char('#'), not_line_ending)),
1050 )))(input)?;
1051 Ok((input, ()))
1052}
1053
1054fn identifier(input: &str) -> IResult<&str, &str> {
1056 recognize(pair(
1057 take_while1(|c: char| c.is_ascii_alphabetic() || c == '_'),
1058 take_while(|c: char| c.is_ascii_alphanumeric() || c == '_'),
1059 ))(input)
1060}
1061
1062fn eng_suffix(input: &str) -> IResult<&str, f64> {
1064 alt((
1065 value(1e-12, tag("p")),
1066 value(1e-9, tag("n")),
1067 value(1e-6, tag("us")), value(1e-6, tag("u")),
1069 value(1e-6, tag("µ")), value(1e3, tag("k")),
1071 value(1e3, tag("K")), value(1e6, tag("M")),
1073 value(1e-3, tag("ms")), value(1e-3, tag("m")), value(1.0, tag("H")), value(1.0, tag("F")), value(1.0, tag("s")), value(1.0, tag("Ω")), value(1.0, tag("R")), ))(input)
1081}
1082
1083fn eng_value(input: &str) -> IResult<&str, f64> {
1088 if let Ok((rest, _)) = tag::<&str, &str, nom::error::Error<&str>>("inf")(input) {
1090 return Ok((rest, f64::INFINITY));
1091 }
1092 let (input, num) = double(input)?;
1094 let (input, mult) = opt(eng_suffix)(input)?;
1095 match mult {
1096 Some(m) => {
1097 let (input, frac_digits) = opt(take_while1(|c: char| c.is_ascii_digit()))(input)?;
1101 if let Some(frac_str) = frac_digits {
1102 let frac: f64 = frac_str.parse().unwrap_or(0.0);
1103 let shift = 10f64.powi(frac_str.len() as i32);
1104 Ok((input, (num + frac / shift) * m))
1105 } else {
1106 Ok((input, num * m))
1107 }
1108 }
1109 None => Ok((input, num)),
1110 }
1111}
1112
1113fn quoted_string(input: &str) -> IResult<&str, &str> {
1115 delimited(char('"'), take_while(|c: char| c != '"'), char('"'))(input)
1116}
1117
1118fn parse_resistor(input: &str) -> IResult<&str, BoxComp> {
1123 let (input, _) = tag("resistor")(input)?;
1124 let (input, _) = char('(')(input)?;
1125 let (input, _) = ws_comments(input)?;
1126 let (input, val) = eng_value(input)?;
1127 let (input, _) = ws_comments(input)?;
1128
1129 let (input, _wattage) = opt(tuple((
1131 char(','),
1132 ws_comments,
1133 eng_value, opt(char('W')),
1135 )))(input)?;
1136
1137 let (input, _) = ws_comments(input)?;
1138 let (input, _) = char(')')(input)?;
1139 Ok((input, Box::new(Resistor { value: val })))
1140}
1141
1142fn cap_type(input: &str) -> IResult<&str, CapType> {
1144 alt((
1145 value(CapType::Film, tag("film")),
1146 value(CapType::Electrolytic, tag("electrolytic")),
1147 value(CapType::Ceramic, tag("ceramic")),
1148 value(CapType::Tantalum, tag("tantalum")),
1149 ))(input)
1150}
1151
1152fn cap_field_leakage(input: &str) -> IResult<&str, f64> {
1154 let (input, _) = tag("leakage")(input)?;
1155 let (input, _) = ws_comments(input)?;
1156 let (input, _) = char(':')(input)?;
1157 let (input, _) = ws_comments(input)?;
1158 let (input, val) = eng_value(input)?;
1159 Ok((input, val))
1160}
1161
1162fn cap_field_da(input: &str) -> IResult<&str, f64> {
1164 let (input, _) = tag("da")(input)?;
1165 let (input, _) = ws_comments(input)?;
1166 let (input, _) = char(':')(input)?;
1167 let (input, _) = ws_comments(input)?;
1168 let (input, val) = double(input)?;
1169 Ok((input, val))
1170}
1171
1172fn parse_cap(input: &str) -> IResult<&str, BoxComp> {
1180 let (input, _) = tag("cap")(input)?;
1181 let (input, _) = char('(')(input)?;
1182 let (input, _) = ws_comments(input)?;
1183 let (input, val) = eng_value(input)?;
1184 let (input, _) = ws_comments(input)?;
1185
1186 let (input, ctype) = opt(preceded(tuple((char(','), ws_comments)), cap_type))(input)?;
1188
1189 let (input, _) = ws_comments(input)?;
1190
1191 let mut leakage = None;
1193 let mut da = None;
1194 let mut input = input;
1195
1196 loop {
1197 let (rest, _) = ws_comments(input)?;
1198
1199 if let Ok((rest2, _)) = char::<_, nom::error::Error<&str>>(',')(rest) {
1201 let (rest3, _) = ws_comments(rest2)?;
1202
1203 if let Ok((rest4, val)) = cap_field_leakage(rest3) {
1205 leakage = Some(val);
1206 input = rest4;
1207 continue;
1208 }
1209 if let Ok((rest4, val)) = cap_field_da(rest3) {
1210 da = Some(val);
1211 input = rest4;
1212 continue;
1213 }
1214 }
1215 break;
1216 }
1217
1218 let (input, _) = ws_comments(input)?;
1219 let (input, _) = char(')')(input)?;
1220
1221 Ok((
1222 input,
1223 Box::new(Capacitor {
1224 config: CapConfig {
1225 value: val,
1226 cap_type: ctype.unwrap_or(CapType::Film),
1227 leakage,
1228 da,
1229 },
1230 }),
1231 ))
1232}
1233
1234fn parse_inductor(input: &str) -> IResult<&str, BoxComp> {
1235 let (input, _) = tag("inductor")(input)?;
1236 let (input, _) = char('(')(input)?;
1237 let (input, val) = eng_value(input)?;
1238 let (input, _) = char(')')(input)?;
1239 Ok((input, Box::new(Inductor { value: val })))
1240}
1241
1242fn diode_type(input: &str) -> IResult<&str, DiodeType> {
1243 alt((
1244 value(DiodeType::Silicon, tag("silicon")),
1245 value(DiodeType::Germanium, tag("germanium")),
1246 value(DiodeType::Led, tag("led")),
1247 value(DiodeType::Schottky, tag("schottky")),
1248 ))(input)
1249}
1250
1251fn parse_diode_pair(input: &str) -> IResult<&str, BoxComp> {
1252 let (input, _) = tag("diode_pair")(input)?;
1253 let (input, _) = char('(')(input)?;
1254 let (input, dt) = diode_type(input)?;
1255 let (input, _) = char(')')(input)?;
1256 Ok((input, Box::new(DiodePair { diode_type: dt })))
1257}
1258
1259fn parse_diode(input: &str) -> IResult<&str, BoxComp> {
1260 let (input, _) = tag("diode")(input)?;
1261 let (input, _) = char('(')(input)?;
1262 let (input, dt) = diode_type(input)?;
1263 let (input, _) = char(')')(input)?;
1264 Ok((input, Box::new(Diode { diode_type: dt })))
1265}
1266
1267fn parse_zener(input: &str) -> IResult<&str, BoxComp> {
1272 let (input, _) = tag("zener")(input)?;
1273 let (input, _) = char('(')(input)?;
1274 let (input, _) = ws_comments(input)?;
1275 let (input, voltage) = double(input)?;
1276 let (input, _) = opt(tag("v"))(input)?;
1278 let (input, _) = ws_comments(input)?;
1279 let (input, _) = char(')')(input)?;
1280 Ok((
1281 input,
1282 Box::new(Zener {
1283 breakdown_voltage: voltage,
1284 }),
1285 ))
1286}
1287
1288fn parse_pot(input: &str) -> IResult<&str, BoxComp> {
1289 let (input, _) = tag("pot")(input)?;
1290 let (input, _) = char('(')(input)?;
1291 let (input, _) = ws_comments(input)?;
1292 let (input, val) = eng_value(input)?;
1293 let (input, _) = ws_comments(input)?;
1294
1295 let (input, taper) = opt(tuple((
1298 char(','),
1299 ws_comments,
1300 alt((
1301 value(PotTaper::A, tag("audio")),
1303 value(PotTaper::B, tag("linear")),
1304 value(PotTaper::A, tag("log")),
1305 value(PotTaper::A, tag("a")),
1307 value(PotTaper::B, tag("b")),
1308 value(PotTaper::C, tag("c")),
1309 )),
1310 )))(input)?;
1311
1312 let taper = taper.map(|(_, _, t)| t).unwrap_or(PotTaper::B);
1313
1314 let (input, _) = ws_comments(input)?;
1315 let (input, _) = char(')')(input)?;
1316 Ok((input, Box::new(Potentiometer { max_r: val, taper })))
1317}
1318
1319fn model_name_str(input: &str) -> IResult<&str, String> {
1321 let (input, name) = take_while1(|c: char| c.is_alphanumeric() || c == '-' || c == '_')(input)?;
1322 Ok((input, name.to_uppercase()))
1323}
1324
1325fn parse_npn(input: &str) -> IResult<&str, BoxComp> {
1326 let (input, _) = tag("npn")(input)?;
1327 let (input, _) = char('(')(input)?;
1328 let (input, _) = ws_comments(input)?;
1329 let (input, name) = opt(model_name_str)(input)?;
1330 let (input, _) = ws_comments(input)?;
1331 let (input, _) = char(')')(input)?;
1332 Ok((
1333 input,
1334 Box::new(Npn {
1335 model: name.unwrap_or_else(|| DEFAULT_NPN_MODEL.to_string()),
1336 }),
1337 ))
1338}
1339
1340fn parse_pnp(input: &str) -> IResult<&str, BoxComp> {
1341 let (input, _) = tag("pnp")(input)?;
1342 let (input, _) = char('(')(input)?;
1343 let (input, _) = ws_comments(input)?;
1344 let (input, name) = opt(model_name_str)(input)?;
1345 let (input, _) = ws_comments(input)?;
1346 let (input, _) = char(')')(input)?;
1347 Ok((
1348 input,
1349 Box::new(Pnp {
1350 model: name.unwrap_or_else(|| DEFAULT_PNP_MODEL.to_string()),
1351 }),
1352 ))
1353}
1354
1355fn opamp_type(input: &str) -> IResult<&str, OpAmpType> {
1356 alt((
1357 value(OpAmpType::Tl071, tag("tl071")),
1360 value(OpAmpType::Tl072, tag("tl072")),
1361 value(OpAmpType::Tl082, tag("tl082")),
1362 value(OpAmpType::Jrc4558, alt((tag("jrc4558"), tag("4558")))),
1363 value(OpAmpType::Rc4558, tag("rc4558")),
1364 value(OpAmpType::Lm308, tag("lm308")),
1365 value(OpAmpType::Lm741, tag("lm741")),
1366 value(
1367 OpAmpType::Ne5532,
1368 alt((tag("njm5532"), tag("ne5532"), tag("5532"))),
1369 ),
1370 value(OpAmpType::Ca3080, tag("ca3080")),
1371 value(OpAmpType::Op07, tag("op07")),
1372 ))(input)
1373}
1374
1375fn parse_opamp(input: &str) -> IResult<&str, BoxComp> {
1376 let (input, _) = tag("opamp")(input)?;
1377 let (input, _) = char('(')(input)?;
1378 let (input, _) = ws_comments(input)?;
1379 let (input, ot) = opt(opamp_type)(input)?;
1380 let (input, _) = ws_comments(input)?;
1381 let (input, _) = char(')')(input)?;
1382 Ok((
1383 input,
1384 Box::new(OpAmp {
1385 op_type: ot.unwrap_or(OpAmpType::Generic),
1386 }),
1387 ))
1388}
1389
1390fn parse_njfet(input: &str) -> IResult<&str, BoxComp> {
1391 let (input, _) = tag("njfet")(input)?;
1392 let (input, _) = char('(')(input)?;
1393 let (input, _) = ws_comments(input)?;
1394 let (input, name) = model_name_str(input)?;
1395 let (input, _) = ws_comments(input)?;
1396 let (input, _) = char(')')(input)?;
1397 Ok((input, Box::new(NJfet { model: name })))
1398}
1399
1400fn parse_pjfet(input: &str) -> IResult<&str, BoxComp> {
1401 let (input, _) = tag("pjfet")(input)?;
1402 let (input, _) = char('(')(input)?;
1403 let (input, _) = ws_comments(input)?;
1404 let (input, name) = model_name_str(input)?;
1405 let (input, _) = ws_comments(input)?;
1406 let (input, _) = char(')')(input)?;
1407 Ok((input, Box::new(PJfet { model: name })))
1408}
1409
1410fn photocoupler_type(input: &str) -> IResult<&str, PhotocouplerType> {
1411 alt((
1412 value(PhotocouplerType::Vtl5c3, tag("vtl5c3")),
1413 value(PhotocouplerType::Vtl5c1, tag("vtl5c1")),
1414 value(PhotocouplerType::Nsl32, tag("nsl32")),
1415 value(PhotocouplerType::T4b, tag("t4b")),
1416 ))(input)
1417}
1418
1419fn parse_photocoupler(input: &str) -> IResult<&str, BoxComp> {
1420 let (input, _) = tag("photocoupler")(input)?;
1421 let (input, _) = char('(')(input)?;
1422 let (input, pt) = photocoupler_type(input)?;
1423 let (input, _) = char(')')(input)?;
1424 Ok((input, Box::new(PhotocouplerComp { coupler_type: pt })))
1425}
1426
1427fn parse_triode(input: &str) -> IResult<&str, BoxComp> {
1428 let (input, _) = tag("triode")(input)?;
1429 let (input, _) = char('(')(input)?;
1430 let (input, _) = ws_comments(input)?;
1431 let (input, name) = model_name_str(input)?;
1432 let (input, _) = ws_comments(input)?;
1433 let (input, _) = char(')')(input)?;
1434 Ok((input, Box::new(Triode { model: name })))
1435}
1436
1437fn parse_pentode(input: &str) -> IResult<&str, BoxComp> {
1438 let (input, _) = tag("pentode")(input)?;
1439 let (input, _) = char('(')(input)?;
1440 let (input, _) = ws_comments(input)?;
1441 let (input, name) = model_name_str(input)?;
1442 let (input, _) = ws_comments(input)?;
1443 let (input, _) = char(')')(input)?;
1444 Ok((input, Box::new(Pentode { model: name })))
1445}
1446
1447fn parse_vari_mu(input: &str) -> IResult<&str, BoxComp> {
1448 let (input, _) = tag("vari_mu")(input)?;
1449 let (input, _) = char('(')(input)?;
1450 let (input, _) = ws_comments(input)?;
1451 let (input, name) = model_name_str(input)?;
1452 let (input, _) = ws_comments(input)?;
1453 let (input, _) = char(')')(input)?;
1454 Ok((input, Box::new(VariMu { model: name })))
1455}
1456
1457fn mosfet_type(input: &str) -> IResult<&str, MosfetType> {
1458 alt((
1459 value(MosfetType::N2n7000, tag("2n7000")),
1460 value(MosfetType::Irf520, tag("irf520")),
1461 value(MosfetType::Bs250, tag("bs250")),
1462 value(MosfetType::Irf9520, tag("irf9520")),
1463 ))(input)
1464}
1465
1466fn parse_nmos(input: &str) -> IResult<&str, BoxComp> {
1467 let (input, _) = tag("nmos")(input)?;
1468 let (input, _) = char('(')(input)?;
1469 let (input, mt) = mosfet_type(input)?;
1470 let (input, _) = char(')')(input)?;
1471 Ok((input, Box::new(Nmos { mosfet_type: mt })))
1472}
1473
1474fn parse_pmos(input: &str) -> IResult<&str, BoxComp> {
1475 let (input, _) = tag("pmos")(input)?;
1476 let (input, _) = char('(')(input)?;
1477 let (input, mt) = mosfet_type(input)?;
1478 let (input, _) = char(')')(input)?;
1479 Ok((input, Box::new(Pmos { mosfet_type: mt })))
1480}
1481
1482fn bbd_type(input: &str) -> IResult<&str, BbdType> {
1483 alt((
1484 value(BbdType::Mn3207, tag("mn3207")),
1485 value(BbdType::Mn3007, tag("mn3007")),
1486 value(BbdType::Mn3005, tag("mn3005")),
1487 ))(input)
1488}
1489
1490fn parse_bbd(input: &str) -> IResult<&str, BoxComp> {
1492 let (input, _) = tag("bbd")(input)?;
1493 let (input, _) = char('(')(input)?;
1494 let (input, bt) = bbd_type(input)?;
1495 let (input, _) = char(')')(input)?;
1496 Ok((input, Box::new(Bbd { bbd_type: bt })))
1497}
1498
1499fn spring_type(input: &str) -> IResult<&str, SpringTankType> {
1503 alt((
1504 value(SpringTankType::Type4, tag("type4")),
1505 value(SpringTankType::Type8, tag("type8")),
1506 value(SpringTankType::Type9, tag("type9")),
1507 value(SpringTankType::Re201Tank, tag("re201_tank")),
1508 ))(input)
1509}
1510
1511fn parse_spring(input: &str) -> IResult<&str, BoxComp> {
1513 let (input, _) = tag("spring")(input)?;
1514 let (input, _) = char('(')(input)?;
1515 let (input, st) = spring_type(input)?;
1516 let (input, _) = char(')')(input)?;
1517 Ok((input, Box::new(Spring { tank_type: st })))
1518}
1519
1520fn interpolation_mode(input: &str) -> IResult<&str, crate::elements::Interpolation> {
1522 alt((
1523 value(crate::elements::Interpolation::Linear, tag("linear")),
1524 value(crate::elements::Interpolation::Allpass, tag("allpass")),
1525 value(crate::elements::Interpolation::Cubic, tag("cubic")),
1526 ))(input)
1527}
1528
1529fn medium_type(input: &str) -> IResult<&str, crate::elements::Medium> {
1531 alt((
1532 value(crate::elements::Medium::None, tag("none")),
1533 value(crate::elements::Medium::TapeOxide, tag("tape_oxide")),
1534 value(crate::elements::Medium::BbdLeakage, tag("bbd_leakage")),
1535 value(
1536 crate::elements::Medium::DigitalQuantize,
1537 tag("digital_quantize"),
1538 ),
1539 ))(input)
1540}
1541
1542fn parse_delay_line(input: &str) -> IResult<&str, BoxComp> {
1550 let (input, _) = tag("delay_line")(input)?;
1551 let (input, _) = char('(')(input)?;
1552 let (input, _) = ws_comments(input)?;
1553 let (input, min_delay) = eng_value(input)?;
1554 let (input, _) = ws_comments(input)?;
1555 let (input, _) = char(',')(input)?;
1556 let (input, _) = ws_comments(input)?;
1557 let (input, max_delay) = eng_value(input)?;
1558 let (input, _) = ws_comments(input)?;
1559 let (input, interp) = opt(preceded(pair(char(','), ws_comments), interpolation_mode))(input)?;
1561 let (input, _) = ws_comments(input)?;
1562 let (input, medium) = opt(preceded(
1564 pair(char(','), ws_comments),
1565 preceded(pair(tag("medium:"), ws_comments), medium_type),
1566 ))(input)?;
1567 let (input, _) = ws_comments(input)?;
1568 let (input, _) = char(')')(input)?;
1569 let interp = interp.unwrap_or(crate::elements::Interpolation::Allpass);
1570 let medium = medium.unwrap_or(crate::elements::Medium::None);
1571 Ok((
1572 input,
1573 Box::new(DelayLineComp {
1574 min_delay,
1575 max_delay,
1576 interpolation: interp,
1577 medium,
1578 }),
1579 ))
1580}
1581
1582fn parse_tap(input: &str) -> IResult<&str, BoxComp> {
1586 let (input, _) = tag("tap")(input)?;
1587 let (input, _) = char('(')(input)?;
1588 let (input, _) = ws_comments(input)?;
1589 let (input, parent_id) = take_while1(|c: char| c.is_alphanumeric() || c == '_')(input)?;
1590 let (input, _) = ws_comments(input)?;
1591 let (input, _) = char(',')(input)?;
1592 let (input, _) = ws_comments(input)?;
1593 let (input, ratio) = double(input)?;
1594 let (input, _) = ws_comments(input)?;
1595 let (input, _) = char(')')(input)?;
1596 Ok((
1597 input,
1598 Box::new(Tap {
1599 parent_id: parent_id.to_string(),
1600 ratio,
1601 }),
1602 ))
1603}
1604
1605fn neon_type(input: &str) -> IResult<&str, NeonType> {
1606 alt((
1607 value(
1608 NeonType::Ne2,
1609 alt((tag("ne2"), tag("ne-2"), tag("NE2"), tag("NE-2"))),
1610 ),
1611 value(
1612 NeonType::Ne51,
1613 alt((tag("ne51"), tag("ne-51"), tag("NE51"), tag("NE-51"))),
1614 ),
1615 value(
1616 NeonType::Ne83,
1617 alt((tag("ne83"), tag("ne-83"), tag("NE83"), tag("NE-83"))),
1618 ),
1619 ))(input)
1620}
1621
1622fn parse_neon(input: &str) -> IResult<&str, BoxComp> {
1625 let (input, _) = tag("neon")(input)?;
1626 let (input, _) = char('(')(input)?;
1627 let (input, _) = ws_comments(input)?;
1628 let (input, nt) = opt(neon_type)(input)?;
1630 let (input, _) = ws_comments(input)?;
1631 let (input, _) = char(')')(input)?;
1632 Ok((
1633 input,
1634 Box::new(Neon {
1635 neon_type: nt.unwrap_or_default(),
1636 }),
1637 ))
1638}
1639
1640fn vco_type(input: &str) -> IResult<&str, VcoType> {
1643 alt((
1644 value(VcoType::Cem3340, tag("cem3340")),
1645 value(VcoType::As3340, tag("as3340")),
1646 value(VcoType::V3340, tag("v3340")),
1647 ))(input)
1648}
1649
1650fn vco_waveform(input: &str) -> IResult<&str, VcoWaveformDsl> {
1651 alt((
1652 value(VcoWaveformDsl::Saw, tag("saw")),
1653 value(VcoWaveformDsl::Triangle, alt((tag("triangle"), tag("tri")))),
1654 value(VcoWaveformDsl::Pulse, alt((tag("pulse"), tag("square")))),
1655 ))(input)
1656}
1657
1658fn parse_vco(input: &str) -> IResult<&str, BoxComp> {
1659 let (input, _) = tag("vco")(input)?;
1660 let (input, _) = char('(')(input)?;
1661 let (input, _) = ws_comments(input)?;
1662 let (input, vt) = vco_type(input)?;
1663 let (input, _) = ws_comments(input)?;
1664 let (input, freq) = opt(preceded(tuple((char(','), ws_comments)), eng_value))(input)?;
1666 let (input, _) = ws_comments(input)?;
1667 let (input, wf) = opt(preceded(tuple((char(','), ws_comments)), vco_waveform))(input)?;
1669 let (input, _) = ws_comments(input)?;
1670 let (input, _) = char(')')(input)?;
1671 Ok((
1672 input,
1673 Box::new(Vco {
1674 vco_type: vt,
1675 base_freq: freq.unwrap_or(440.0),
1676 waveform: wf.unwrap_or(VcoWaveformDsl::Saw),
1677 }),
1678 ))
1679}
1680
1681fn vcf_type(input: &str) -> IResult<&str, VcfType> {
1682 alt((
1683 value(VcfType::Cem3320, tag("cem3320")),
1684 value(VcfType::As3320, tag("as3320")),
1685 ))(input)
1686}
1687
1688fn parse_vcf(input: &str) -> IResult<&str, BoxComp> {
1689 let (input, _) = tag("vcf")(input)?;
1690 let (input, _) = char('(')(input)?;
1691 let (input, _) = ws_comments(input)?;
1692 let (input, vt) = vcf_type(input)?;
1693 let (input, _) = ws_comments(input)?;
1694 let (input, _) = char(')')(input)?;
1695 Ok((input, Box::new(Vcf { vcf_type: vt })))
1696}
1697
1698fn vca_type(input: &str) -> IResult<&str, VcaType> {
1699 alt((
1700 value(VcaType::Ssm2164, tag("ssm2164")),
1701 value(VcaType::V2164, tag("v2164")),
1702 ))(input)
1703}
1704
1705fn parse_vca(input: &str) -> IResult<&str, BoxComp> {
1706 let (input, _) = tag("vca")(input)?;
1707 let (input, _) = char('(')(input)?;
1708 let (input, _) = ws_comments(input)?;
1709 let (input, vt) = vca_type(input)?;
1710 let (input, _) = ws_comments(input)?;
1711 let (input, _) = char(')')(input)?;
1712 Ok((input, Box::new(Vca { vca_type: vt })))
1713}
1714
1715fn comparator_type(input: &str) -> IResult<&str, ComparatorType> {
1716 alt((
1717 value(ComparatorType::Lm311, tag("lm311")),
1718 value(ComparatorType::Lm393, tag("lm393")),
1719 ))(input)
1720}
1721
1722fn parse_comparator(input: &str) -> IResult<&str, BoxComp> {
1723 let (input, _) = tag("comparator")(input)?;
1724 let (input, _) = char('(')(input)?;
1725 let (input, _) = ws_comments(input)?;
1726 let (input, ct) = comparator_type(input)?;
1727 let (input, _) = ws_comments(input)?;
1728 let (input, _) = char(')')(input)?;
1729 Ok((input, Box::new(Comparator { comp_type: ct })))
1730}
1731
1732fn analog_switch_type(input: &str) -> IResult<&str, AnalogSwitchType> {
1733 alt((
1734 value(AnalogSwitchType::Cd4066, tag("cd4066")),
1735 value(AnalogSwitchType::Dg411, tag("dg411")),
1736 ))(input)
1737}
1738
1739fn parse_analog_switch(input: &str) -> IResult<&str, BoxComp> {
1740 let (input, _) = tag("switch")(input)?;
1741 let (input, _) = char('(')(input)?;
1742 let (input, _) = ws_comments(input)?;
1743 let (input, st) = analog_switch_type(input)?;
1744 let (input, _) = ws_comments(input)?;
1745 let (input, _) = char(')')(input)?;
1746 Ok((input, Box::new(AnalogSwitch { switch_type: st })))
1747}
1748
1749fn matched_transistor_type(input: &str) -> IResult<&str, MatchedTransistorType> {
1750 alt((
1751 value(MatchedTransistorType::Ssm2210, tag("ssm2210")),
1752 value(MatchedTransistorType::Ca3046, tag("ca3046")),
1753 value(MatchedTransistorType::Lm394, tag("lm394")),
1754 value(MatchedTransistorType::That340, tag("that340")),
1755 ))(input)
1756}
1757
1758fn parse_matched_npn(input: &str) -> IResult<&str, BoxComp> {
1759 let (input, _) = tag("matched_npn")(input)?;
1760 let (input, _) = char('(')(input)?;
1761 let (input, _) = ws_comments(input)?;
1762 let (input, mt) = matched_transistor_type(input)?;
1763 let (input, _) = ws_comments(input)?;
1764 let (input, _) = char(')')(input)?;
1765 Ok((input, Box::new(MatchedNpn { matched_type: mt })))
1766}
1767
1768fn parse_matched_pnp(input: &str) -> IResult<&str, BoxComp> {
1769 let (input, _) = tag("matched_pnp")(input)?;
1770 let (input, _) = char('(')(input)?;
1771 let (input, _) = ws_comments(input)?;
1772 let (input, mt) = matched_transistor_type(input)?;
1773 let (input, _) = ws_comments(input)?;
1774 let (input, _) = char(')')(input)?;
1775 Ok((input, Box::new(MatchedPnp { matched_type: mt })))
1776}
1777
1778fn parse_tempco(input: &str) -> IResult<&str, BoxComp> {
1780 let (input, _) = tag("tempco")(input)?;
1781 let (input, _) = char('(')(input)?;
1782 let (input, _) = ws_comments(input)?;
1783 let (input, resistance) = eng_value(input)?;
1784 let (input, _) = ws_comments(input)?;
1785 let (input, _) = char(',')(input)?;
1786 let (input, _) = ws_comments(input)?;
1787 let (input, ppm) = double(input)?;
1788 let (input, _) = ws_comments(input)?;
1789 let (input, _) = char(')')(input)?;
1790 Ok((input, Box::new(Tempco { resistance, ppm })))
1791}
1792
1793fn winding_modifier(input: &str) -> IResult<&str, WindingType> {
1801 alt((
1802 value(WindingType::CenterTap, tag("ct")),
1803 value(WindingType::PushPull, tag("pp")),
1804 ))(input)
1805}
1806
1807fn winding_modifier_primary(input: &str) -> IResult<&str, WindingType> {
1809 value(WindingType::CenterTap, tag("ct_primary"))(input)
1810}
1811
1812fn parse_transformer(input: &str) -> IResult<&str, BoxComp> {
1825 let (input, _) = tag("transformer")(input)?;
1826 let (input, _) = char('(')(input)?;
1827 let (input, _) = ws_comments(input)?;
1828
1829 let (input, ratio_num) = double(input)?;
1831 let (input, ratio) = if let Ok((input, _)) = char::<&str, nom::error::Error<&str>>(':')(input) {
1832 let (input, denom) = double(input)?;
1833 (input, ratio_num / denom)
1834 } else {
1835 (input, ratio_num)
1836 };
1837
1838 let (input, _) = ws_comments(input)?;
1839 let (input, _) = char(',')(input)?;
1840 let (input, _) = ws_comments(input)?;
1841
1842 let (input, mut config) = if let Ok((input, inductance)) = eng_value(input) {
1844 (input, TransformerConfig::new(ratio, inductance))
1845 } else {
1846 let (input, model) = model_name_str(input)?;
1847 (input, TransformerConfig::with_model(ratio, model))
1848 };
1849 let (input, _) = ws_comments(input)?;
1850
1851 let mut positional_count = 0; let mut remaining = input;
1856 loop {
1857 if let Ok((input, _)) = tuple((
1859 ws_comments,
1860 char::<&str, nom::error::Error<&str>>(','),
1861 ws_comments,
1862 ))(remaining)
1863 {
1864 remaining = input;
1865
1866 if let Ok((input, wt)) = winding_modifier_primary(remaining) {
1868 config.primary_type = wt;
1869 remaining = input;
1870 continue;
1871 }
1872
1873 if let Ok((input, wt)) = winding_modifier(remaining) {
1875 if wt == WindingType::PushPull {
1878 config.primary_type = wt;
1879 } else {
1880 if config.secondary_type == WindingType::Standard {
1882 config.secondary_type = wt;
1883 } else {
1884 config.primary_type = wt;
1885 }
1886 }
1887 remaining = input;
1888 continue;
1889 }
1890
1891 if let Ok((input, _)) = tag::<&str, &str, nom::error::Error<&str>>("dcr")(remaining) {
1893 let (input, _) = ws_comments(input)?;
1894 let (input, _) = char('=')(input)?;
1895 let (input, _) = ws_comments(input)?;
1896 let (input, dcr) = eng_value(input)?;
1897 config.primary_dcr = dcr;
1898 config.secondary_dcr = dcr; remaining = input;
1900 continue;
1901 }
1902
1903 if let Ok((input, _)) =
1904 alt((tag::<&str, &str, nom::error::Error<&str>>("Cp"), tag("cp")))(remaining)
1905 {
1906 let (input, _) = ws_comments(input)?;
1907 let (input, _) = char('=')(input)?;
1908 let (input, _) = ws_comments(input)?;
1909 let (input, cap) = eng_value(input)?;
1910 config.capacitance = cap;
1911 remaining = input;
1912 continue;
1913 }
1914
1915 if let Ok((input, _)) = tag::<&str, &str, nom::error::Error<&str>>("k")(remaining) {
1916 let (input, _) = ws_comments(input)?;
1917 let (input, _) = char('=')(input)?;
1918 let (input, _) = ws_comments(input)?;
1919 let (input, k) = double(input)?;
1920 config.coupling = k;
1921 remaining = input;
1922 continue;
1923 }
1924
1925 if let Ok((input, name)) = alt((
1926 tag::<&str, &str, nom::error::Error<&str>>("Llp"),
1927 tag("llp"),
1928 tag("Lls"),
1929 tag("lls"),
1930 tag("Lm"),
1931 tag("lm"),
1932 tag("Lp"),
1933 tag("lp"),
1934 tag("Rc"),
1935 tag("rc"),
1936 tag("Idc"),
1937 tag("idc"),
1938 ))(remaining)
1939 {
1940 let (input, _) = ws_comments(input)?;
1941 let (input, _) = char('=')(input)?;
1942 let (input, _) = ws_comments(input)?;
1943 let (input, value) = eng_value(input)?;
1944 match name {
1945 "Llp" | "llp" => config.primary_leakage = Some(value),
1946 "Lls" | "lls" => config.secondary_leakage = Some(value),
1947 "Lm" | "lm" => config.magnetizing_inductance = Some(value),
1948 "Lp" | "lp" => config.primary_inductance = value,
1949 "Rc" | "rc" => config.core_loss_resistance = Some(value),
1950 "Idc" | "idc" => config.dc_bias_current = Some(value),
1951 _ => unreachable!(),
1952 }
1953 remaining = input;
1954 continue;
1955 }
1956
1957 if let Ok((input, _)) =
1959 tag::<&str, &str, nom::error::Error<&str>>("tertiary")(remaining)
1960 {
1961 let (input, _) = ws_comments(input)?;
1962 let (input, _) = char('=')(input)?;
1963 let (input, _) = ws_comments(input)?;
1964 let (input, t_num) = double(input)?;
1965 let (input, t_ratio) =
1966 if let Ok((input, _)) = char::<&str, nom::error::Error<&str>>(':')(input) {
1967 let (input, denom) = double(input)?;
1968 (input, t_num / denom)
1969 } else {
1970 (input, t_num)
1971 };
1972 config.tertiary_turns_ratio = Some(t_ratio);
1973 remaining = input;
1974 continue;
1975 }
1976
1977 if let Ok((input, val)) = eng_value(remaining) {
1979 match positional_count {
1980 0 => {
1981 config.primary_dcr = val;
1983 config.secondary_dcr = val;
1984 }
1985 1 => {
1986 config.capacitance = val;
1988 }
1989 _ => {
1990 break;
1992 }
1993 }
1994 positional_count += 1;
1995 remaining = input;
1996 continue;
1997 }
1998
1999 break;
2001 } else {
2002 break;
2003 }
2004 }
2005
2006 let (input, _) = ws_comments(remaining)?;
2007 let (input, _) = char(')')(input)?;
2008
2009 Ok((input, Box::new(TransformerComp { config })))
2010}
2011
2012fn parse_tape_head(input: &str) -> IResult<&str, BoxComp> {
2024 let (input, _) = tag("tape_head")(input)?;
2025 let (input, _) = char('(')(input)?;
2026 let (input, _) = ws_comments(input)?;
2027
2028 let mut config = TapeHeadConfig::studio_head();
2029
2030 fn override_key(input: &str) -> IResult<&str, &str> {
2034 let (rest, name) = alt((
2035 tag("isat"),
2036 tag("alpha"),
2037 tag("h_bias"),
2038 tag("kv"),
2039 tag("gp"),
2040 tag("rp"),
2041 tag("ms"),
2042 tag("a"),
2043 tag("k"),
2044 tag("c"),
2045 ))(input)?;
2046 let (rest, _) = ws_comments(rest)?;
2047 let (rest, _) = peek(char('='))(rest)?;
2048 Ok((rest, name))
2049 }
2050
2051 let input = if peek(override_key)(input).is_ok() {
2053 input
2054 } else if let Ok((input, model)) = model_name_str(input) {
2055 config = TapeHeadConfig::with_model(model.to_string());
2056 input
2057 } else {
2058 input
2059 };
2060 let (input, _) = ws_comments(input)?;
2061
2062 let mut remaining = input;
2064 loop {
2065 if let Ok((input, _)) = tuple((
2066 ws_comments,
2067 char::<&str, nom::error::Error<&str>>(','),
2068 ws_comments,
2069 ))(remaining)
2070 {
2071 remaining = input;
2072 }
2073
2074 if let Ok((input, name)) = override_key(remaining) {
2075 let (input, _) = char('=')(input)?;
2076 let (input, _) = ws_comments(input)?;
2077 let (input, value) = eng_value(input)?;
2078 match name {
2079 "kv" => config.kv = value,
2080 "isat" => config.isat = value,
2081 "gp" => config.gp = value,
2082 "rp" => config.rp = value,
2083 "h_bias" => config.h_bias = value,
2084 "ms" => config.ja_ms = value,
2085 "a" => config.ja_a = value,
2086 "alpha" => config.ja_alpha = value,
2087 "k" => config.ja_k = value,
2088 "c" => config.ja_c = value,
2089 _ => unreachable!(),
2090 }
2091 remaining = input;
2092 continue;
2093 }
2094 break;
2095 }
2096
2097 let (input, _) = ws_comments(remaining)?;
2098 let (input, _) = char(')')(input)?;
2099
2100 Ok((input, Box::new(TapeHeadComp { config })))
2101}
2102
2103fn parse_cap_switched(input: &str) -> IResult<&str, BoxComp> {
2105 let (input, _) = tag("cap_switched")(input)?;
2106 let (input, _) = char('(')(input)?;
2107 let (input, _) = ws_comments(input)?;
2108
2109 let (input, has_bracket) = opt(char('['))(input)?;
2111 let (input, _) = ws_comments(input)?;
2112
2113 let (input, values) =
2115 separated_list1(tuple((ws_comments, char(','), ws_comments)), eng_value)(input)?;
2116
2117 let (input, _) = ws_comments(input)?;
2118
2119 let input = if has_bracket.is_some() {
2121 let (input, _) = char(']')(input)?;
2122 let (input, _) = ws_comments(input)?;
2123 input
2124 } else {
2125 input
2126 };
2127
2128 let (input, _) = char(')')(input)?;
2129
2130 Ok((input, Box::new(CapSwitched { values })))
2131}
2132
2133fn parse_inductor_switched(input: &str) -> IResult<&str, BoxComp> {
2136 let (input, _) = tag("inductor_switched")(input)?;
2137 let (input, _) = char('(')(input)?;
2138 let (input, _) = ws_comments(input)?;
2139
2140 let (input, has_bracket) = opt(char('['))(input)?;
2142 let (input, _) = ws_comments(input)?;
2143
2144 let (input, values) =
2146 separated_list1(tuple((ws_comments, char(','), ws_comments)), eng_value)(input)?;
2147
2148 let (input, _) = ws_comments(input)?;
2149
2150 let input = if has_bracket.is_some() {
2152 let (input, _) = char(']')(input)?;
2153 let (input, _) = ws_comments(input)?;
2154 input
2155 } else {
2156 input
2157 };
2158
2159 let (input, _) = char(')')(input)?;
2160
2161 Ok((input, Box::new(InductorSwitched { values })))
2162}
2163
2164fn parse_resistor_switched(input: &str) -> IResult<&str, BoxComp> {
2167 let (input, _) = tag("resistor_switched")(input)?;
2168 let (input, _) = char('(')(input)?;
2169 let (input, _) = ws_comments(input)?;
2170
2171 let (input, has_bracket) = opt(char('['))(input)?;
2173 let (input, _) = ws_comments(input)?;
2174
2175 let (input, values) =
2177 separated_list1(tuple((ws_comments, char(','), ws_comments)), eng_value)(input)?;
2178
2179 let (input, _) = ws_comments(input)?;
2180
2181 let input = if has_bracket.is_some() {
2183 let (input, _) = char(']')(input)?;
2184 let (input, _) = ws_comments(input)?;
2185 input
2186 } else {
2187 input
2188 };
2189
2190 let (input, _) = char(')')(input)?;
2191
2192 Ok((input, Box::new(ResistorSwitched { values })))
2193}
2194
2195fn parse_trigger_input(input: &str) -> IResult<&str, BoxComp> {
2199 let (rest, _) = tag("trigger_input()")(input)?;
2200 Ok((rest, Box::new(TriggerInputComp)))
2201}
2202
2203fn parse_switch(input: &str) -> IResult<&str, BoxComp> {
2204 let (input, _) = tag("switch")(input)?;
2205 let (input, _) = char('(')(input)?;
2206 let (input, _) = ws_comments(input)?;
2207 let (input, positions) = double(input)?;
2208 let (input, _) = ws_comments(input)?;
2209 let (input, _) = char(')')(input)?;
2210
2211 Ok((
2212 input,
2213 Box::new(Switch {
2214 positions: positions as usize,
2215 }),
2216 ))
2217}
2218
2219fn parse_rotary_switch(input: &str) -> IResult<&str, BoxComp> {
2222 let (input, _) = tag("rotary")(input)?;
2223 let (input, _) = char('(')(input)?;
2224 let (input, _) = ws_comments(input)?;
2225
2226 let (input, labels) = separated_list1(
2228 tuple((ws_comments, char(','), ws_comments)),
2229 alt((
2230 map(quoted_string, String::from),
2231 map(identifier, String::from),
2232 )),
2233 )(input)?;
2234
2235 let (input, _) = ws_comments(input)?;
2236 let (input, _) = char(')')(input)?;
2237
2238 Ok((input, Box::new(RotarySwitch { linked_ids: labels })))
2239}
2240
2241fn component_kind(input: &str) -> IResult<&str, BoxComp> {
2242 alt((
2243 alt((
2244 parse_resistor_switched, parse_resistor,
2246 parse_cap_switched, parse_cap,
2248 parse_inductor_switched, parse_inductor,
2250 parse_diode_pair, parse_diode,
2252 parse_zener, parse_pot,
2254 )),
2255 alt((
2256 parse_npn,
2257 parse_pnp,
2258 parse_opamp,
2259 parse_njfet,
2260 parse_pjfet,
2261 parse_photocoupler,
2262 parse_envelope_follower, parse_lfo,
2264 parse_triode,
2265 parse_pentode,
2266 parse_vari_mu,
2267 )),
2268 alt((
2269 parse_nmos,
2270 parse_pmos,
2271 parse_bbd,
2272 parse_spring,
2273 parse_delay_line,
2274 parse_tap,
2275 parse_neon,
2276 parse_vco,
2277 parse_vcf,
2278 parse_vca,
2279 parse_comparator,
2280 )),
2281 alt((
2282 parse_analog_switch,
2283 parse_matched_npn, parse_matched_pnp,
2285 parse_tempco,
2286 parse_tape_head, parse_transformer,
2288 parse_rotary_switch,
2289 parse_switch, parse_trigger_input,
2291 )),
2292 ))(input)
2293}
2294
2295fn component_def(input: &str) -> IResult<&str, (ComponentDef, Option<String>)> {
2297 let (input, _) = ws_comments(input)?;
2298 let (input, id) = identifier(input)?;
2299 let (input, _) = ws_comments(input)?;
2300 let (input, _) = char(':')(input)?;
2301 let (input, _) = ws_comments(input)?;
2302 let (input, kind) = component_kind(input)?;
2303 let (input, mirrors_target) = opt(tuple((
2305 ws_comments,
2306 tag("mirrors"),
2307 ws_comments,
2308 identifier,
2309 )))(input)?;
2310 let mirrors = mirrors_target.map(|(_, _, _, target)| target.to_string());
2311 Ok((
2312 input,
2313 (
2314 ComponentDef {
2315 id: id.to_string(),
2316 kind,
2317 },
2318 mirrors,
2319 ),
2320 ))
2321}
2322
2323const RESERVED_NODES: &[&str] = &[
2328 "in",
2329 "out",
2330 "gnd",
2331 "vcc",
2332 "fx_send",
2333 "fx_return",
2334 "gate", "cv_pitch", "cv_mod", "cv_filter", ];
2340
2341fn identifier_or_supply_rail(input: &str) -> IResult<&str, &str> {
2344 alt((
2345 recognize(pair(alpha1, alt((char('+'), char('-'))))),
2347 identifier,
2349 ))(input)
2350}
2351
2352fn pin(input: &str) -> IResult<&str, Pin> {
2353 let (input, first) = identifier_or_supply_rail(input)?;
2354
2355 let (input, rest_parts) = many0(preceded(char('.'), identifier))(input)?;
2357
2358 if rest_parts.is_empty() {
2359 if RESERVED_NODES.contains(&first) {
2361 Ok((input, Pin::Reserved(first.to_string())))
2362 } else {
2363 Ok((input, Pin::Reserved(first.to_string())))
2365 }
2366 } else {
2367 let pin_name = rest_parts.join(".");
2370 Ok((
2371 input,
2372 Pin::ComponentPin {
2373 component: first.to_string(),
2374 pin: pin_name,
2375 },
2376 ))
2377 }
2378}
2379
2380fn parse_fork(input: &str) -> IResult<&str, Pin> {
2383 let (input, _) = tag("fork")(input)?;
2384 let (input, _) = char('(')(input)?;
2385 let (input, _) = ws_comments(input)?;
2386 let (input, switch_id) = identifier(input)?;
2388 let (input, _) = ws_comments(input)?;
2389 let (input, _) = char(',')(input)?;
2390 let (input, _) = ws_comments(input)?;
2391 let (input, _) = char('[')(input)?;
2393 let (input, _) = ws_comments(input)?;
2394 let (input, destinations) =
2395 separated_list1(tuple((ws_comments, char(','), ws_comments)), pin)(input)?;
2396 let (input, _) = ws_comments(input)?;
2397 let (input, _) = char(']')(input)?;
2398 let (input, _) = ws_comments(input)?;
2399 let (input, _) = char(')')(input)?;
2400 Ok((
2401 input,
2402 Pin::Fork {
2403 switch: switch_id.to_string(),
2404 destinations,
2405 },
2406 ))
2407}
2408
2409fn dest_pin(input: &str) -> IResult<&str, Pin> {
2411 alt((parse_fork, pin))(input)
2412}
2413
2414fn net_def(input: &str) -> IResult<&str, NetDef> {
2416 let (input, _) = ws_comments(input)?;
2417 let (input, from) = pin(input)?;
2418 let (input, _) = ws_comments(input)?;
2419 let (input, _) = tag("->")(input)?;
2420 let (input, _) = ws_comments(input)?;
2421 let (input, to) =
2423 separated_list1(tuple((ws_comments, char(','), ws_comments)), dest_pin)(input)?;
2424 Ok((input, NetDef { from, to }))
2425}
2426
2427fn control_def(input: &str) -> IResult<&str, ControlDef> {
2433 let (input, _) = ws_comments(input)?;
2434 let (input, comp) = identifier(input)?;
2435 let (input, _) = char('.')(input)?;
2436 let (input, prop) = identifier(input)?;
2437 let (input, _) = ws_comments(input)?;
2438 let (input, _) = tag("->")(input)?;
2439 let (input, _) = ws_comments(input)?;
2440 let (input, label) = quoted_string(input)?;
2441 let (input, _) = ws_comments(input)?;
2442 let (input, _) = char('[')(input)?;
2444 let (input, _) = ws_comments(input)?;
2445 let (input, lo) = double(input)?;
2446 let (input, _) = ws_comments(input)?;
2447 let (input, _) = char(',')(input)?;
2448 let (input, _) = ws_comments(input)?;
2449 let (input, hi) = double(input)?;
2450 let (input, _) = ws_comments(input)?;
2451 let (input, _) = char(']')(input)?;
2452 let (input, _) = ws_comments(input)?;
2453 let (input, _) = char('=')(input)?;
2454 let (input, _) = ws_comments(input)?;
2455 let (input, def) = double(input)?;
2456 Ok((
2457 input,
2458 ControlDef {
2459 component: comp.to_string(),
2460 property: prop.to_string(),
2461 label: label.to_string(),
2462 range: (lo, hi),
2463 default: def,
2464 },
2465 ))
2466}
2467
2468fn lfo_waveform(input: &str) -> IResult<&str, LfoWaveformDsl> {
2469 alt((
2470 value(LfoWaveformDsl::Sine, tag("sine")),
2471 value(LfoWaveformDsl::Triangle, tag("triangle")),
2472 value(LfoWaveformDsl::Square, tag("square")),
2473 value(LfoWaveformDsl::SawUp, tag("saw_up")),
2474 value(LfoWaveformDsl::SawDown, tag("saw_down")),
2475 value(LfoWaveformDsl::SampleAndHold, tag("sample_hold")),
2476 ))(input)
2477}
2478
2479fn parse_envelope_follower(input: &str) -> IResult<&str, BoxComp> {
2483 let (input, _) = tag("envelope_follower")(input)?;
2484 let (input, _) = char('(')(input)?;
2485 let (input, _) = ws_comments(input)?;
2486 let (input, attack_r) = eng_value(input)?;
2487 let (input, _) = ws_comments(input)?;
2488 let (input, _) = char(',')(input)?;
2489 let (input, _) = ws_comments(input)?;
2490 let (input, attack_c) = eng_value(input)?;
2491 let (input, _) = ws_comments(input)?;
2492 let (input, _) = char(',')(input)?;
2493 let (input, _) = ws_comments(input)?;
2494 let (input, release_r) = eng_value(input)?;
2495 let (input, _) = ws_comments(input)?;
2496 let (input, _) = char(',')(input)?;
2497 let (input, _) = ws_comments(input)?;
2498 let (input, release_c) = eng_value(input)?;
2499 let (input, _) = ws_comments(input)?;
2500 let (input, _) = char(',')(input)?;
2501 let (input, _) = ws_comments(input)?;
2502 let (input, sensitivity_r) = eng_value(input)?;
2503 let (input, _) = ws_comments(input)?;
2504 let (input, _) = char(')')(input)?;
2505 Ok((
2506 input,
2507 Box::new(EnvelopeFollower {
2508 attack_r,
2509 attack_c,
2510 release_r,
2511 release_c,
2512 sensitivity_r,
2513 }),
2514 ))
2515}
2516
2517fn parse_lfo(input: &str) -> IResult<&str, BoxComp> {
2520 let (input, _) = tag("lfo")(input)?;
2521 let (input, _) = char('(')(input)?;
2522 let (input, _) = ws_comments(input)?;
2523 let (input, waveform) = lfo_waveform(input)?;
2524 let (input, _) = ws_comments(input)?;
2525 let (input, _) = char(',')(input)?;
2526 let (input, _) = ws_comments(input)?;
2527 let (input, timing_r) = eng_value(input)?;
2528 let (input, _) = ws_comments(input)?;
2529 let (input, _) = char(',')(input)?;
2530 let (input, _) = ws_comments(input)?;
2531 let (input, timing_c) = eng_value(input)?;
2532 let (input, _) = ws_comments(input)?;
2533 let (input, _) = char(')')(input)?;
2534 Ok((
2535 input,
2536 Box::new(Lfo {
2537 waveform,
2538 timing_r,
2539 timing_c,
2540 }),
2541 ))
2542}
2543
2544enum ComponentEntry {
2552 Component(ComponentDef, Option<String>),
2553 Use(UseInstance),
2554}
2555
2556fn override_entry(input: &str) -> IResult<&str, (String, f64)> {
2558 let (input, _) = ws_comments(input)?;
2559 let (input, comp_id) = identifier(input)?;
2560 let (input, _) = ws_comments(input)?;
2561 let (input, _) = char(':')(input)?;
2562 let (input, _) = ws_comments(input)?;
2563 let (input, val) = eng_value(input)?;
2564 Ok((input, (comp_id.to_string(), val)))
2565}
2566
2567fn with_overrides(input: &str) -> IResult<&str, Vec<(String, f64)>> {
2570 let (input, _) = ws_comments(input)?;
2571 if let Ok((input2, _)) = tag::<&str, &str, nom::error::Error<&str>>("with")(input) {
2573 let (input2, _) = ws_comments(input2)?;
2574 let (input2, _) = char('{')(input2)?;
2575 let mut overrides = Vec::new();
2577 let mut input2 = input2;
2578 loop {
2579 let (rest, _) = ws_comments(input2)?;
2580 let rest = if let Ok((r, _)) =
2582 char::<&str, nom::error::Error<&str>>(',')(rest)
2583 {
2584 r
2585 } else {
2586 rest
2587 };
2588 let (rest, _) = ws_comments(rest)?;
2589 if rest.starts_with('}') {
2591 input2 = rest;
2592 break;
2593 }
2594 match override_entry(rest) {
2595 Ok((r, entry)) => {
2596 overrides.push(entry);
2597 input2 = r;
2598 }
2599 Err(_) => {
2600 input2 = rest;
2601 break;
2602 }
2603 }
2604 }
2605 let (input2, _) = ws_comments(input2)?;
2606 let (input2, _) = char('}')(input2)?;
2607 Ok((input2, overrides))
2608 } else {
2609 Ok((input, Vec::new()))
2610 }
2611}
2612
2613fn use_instance(input: &str) -> IResult<&str, UseInstance> {
2619 let (input, _) = ws_comments(input)?;
2620 let (input, id) = identifier(input)?;
2621 let (input, _) = ws_comments(input)?;
2622 let (input, _) = char(':')(input)?;
2623 let (input, _) = ws_comments(input)?;
2624 let (input, _) = tag("use")(input)?;
2625 let (input, _) = ws_comments(input)?;
2626 let (input, _) = char('(')(input)?;
2627 let (input, _) = ws_comments(input)?;
2628 let (input, path) = quoted_string(input)?;
2629 let (input, _) = ws_comments(input)?;
2630 let (input, _) = char(')')(input)?;
2631 let (input, overrides) = with_overrides(input)?;
2632 Ok((
2633 input,
2634 UseInstance {
2635 id: id.to_string(),
2636 path: path.to_string(),
2637 overrides,
2638 },
2639 ))
2640}
2641
2642fn component_entry(input: &str) -> IResult<&str, ComponentEntry> {
2643 alt((
2644 nom::combinator::map(use_instance, ComponentEntry::Use),
2645 nom::combinator::map(component_def, |(c, m)| ComponentEntry::Component(c, m)),
2646 ))(input)
2647}
2648
2649fn components_section(
2650 input: &str,
2651) -> IResult<
2652 &str,
2653 (
2654 Vec<ComponentDef>,
2655 hashbrown::HashMap<String, String>,
2656 Vec<UseInstance>,
2657 ),
2658> {
2659 let (input, _) = ws_comments(input)?;
2660 let (input, _) = tag("components")(input)?;
2661 let (input, _) = ws_comments(input)?;
2662 let (input, _) = char('{')(input)?;
2663 let (input, entries) = many0(component_entry)(input)?;
2664 let (input, _) = ws_comments(input)?;
2665 let (input, _) = char('}')(input)?;
2666 let mut mirrors = hashbrown::HashMap::new();
2667 let mut comps = Vec::new();
2668 let mut uses = Vec::new();
2669 for entry in entries {
2670 match entry {
2671 ComponentEntry::Component(comp, mirror_target) => {
2672 if let Some(target) = mirror_target {
2673 mirrors.insert(comp.id.clone(), target);
2674 }
2675 comps.push(comp);
2676 }
2677 ComponentEntry::Use(u) => uses.push(u),
2678 }
2679 }
2680 Ok((input, (comps, mirrors, uses)))
2681}
2682
2683fn nets_section(input: &str) -> IResult<&str, Vec<NetDef>> {
2684 let (input, _) = ws_comments(input)?;
2685 let (input, _) = tag("nets")(input)?;
2686 let (input, _) = ws_comments(input)?;
2687 let (input, _) = char('{')(input)?;
2688 let (input, nets) = many0(net_def)(input)?;
2689 let (input, _) = ws_comments(input)?;
2690 let (input, _) = char('}')(input)?;
2691 Ok((input, nets))
2692}
2693
2694fn skip_line(input: &str) -> IResult<&str, ()> {
2696 let (input, _) = ws_comments(input)?;
2697 let (input, _) = take_while(|c: char| c != '\n' && c != '}')(input)?;
2699 let (input, _) = opt(char('\n'))(input)?;
2701 Ok((input, ()))
2702}
2703
2704fn control_or_skip(input: &str) -> IResult<&str, Option<ControlDef>> {
2706 let (input, _) = ws_comments(input)?;
2707
2708 if input.starts_with('}') {
2710 return Err(nom::Err::Error(nom::error::Error::new(
2711 input,
2712 nom::error::ErrorKind::Char,
2713 )));
2714 }
2715
2716 if let Ok((remaining, ctrl)) = control_def(input) {
2718 return Ok((remaining, Some(ctrl)));
2719 }
2720
2721 let (remaining, _) = skip_line(input)?;
2723 Ok((remaining, None))
2724}
2725
2726fn port_def(input: &str) -> IResult<&str, PortDef> {
2732 let (input, _) = ws_comments(input)?;
2733 let (input, name) = identifier(input)?;
2734 let (input, _) = ws_comments(input)?;
2735 let (input, _) = char(':')(input)?;
2736 let (input, _) = ws_comments(input)?;
2737 let (input, dir_str) = alt((tag("input"), tag("output")))(input)?;
2738 let direction = match dir_str {
2739 "input" => pedalkernel_rt::PortDirection::Input,
2740 "output" => pedalkernel_rt::PortDirection::Output,
2741 _ => unreachable!(),
2742 };
2743 let (input, impedance) = opt(delimited(char('('), eng_value, char(')')))(input)?;
2745 Ok((
2746 input,
2747 PortDef {
2748 name: name.to_string(),
2749 direction,
2750 impedance,
2751 },
2752 ))
2753}
2754
2755fn ports_section(input: &str) -> IResult<&str, Vec<PortDef>> {
2757 let (input, _) = ws_comments(input)?;
2758 let (input, _) = tag("ports")(input)?;
2759 let (input, _) = ws_comments(input)?;
2760 let (input, _) = char('{')(input)?;
2761 let (input, ports) = many0(port_def)(input)?;
2762 let (input, _) = ws_comments(input)?;
2763 let (input, _) = char('}')(input)?;
2764 Ok((input, ports))
2765}
2766
2767fn controls_section(input: &str) -> IResult<&str, Vec<ControlDef>> {
2768 let (input, _) = ws_comments(input)?;
2769 let (input, _) = tag("controls")(input)?;
2770 let (input, _) = ws_comments(input)?;
2771 let (input, _) = char('{')(input)?;
2772
2773 let (input, items) = many0(control_or_skip)(input)?;
2775 let ctrls: Vec<ControlDef> = items.into_iter().flatten().collect();
2776
2777 let (input, _) = ws_comments(input)?;
2778 let (input, _) = char('}')(input)?;
2779 Ok((input, ctrls))
2780}
2781
2782fn trims_section(input: &str) -> IResult<&str, Vec<ControlDef>> {
2785 let (input, _) = ws_comments(input)?;
2786 let (input, _) = tag("trims")(input)?;
2787 let (input, _) = ws_comments(input)?;
2788 let (input, _) = char('{')(input)?;
2789
2790 let (input, items) = many0(control_or_skip)(input)?;
2792 let trims: Vec<ControlDef> = items.into_iter().flatten().collect();
2793
2794 let (input, _) = ws_comments(input)?;
2795 let (input, _) = char('}')(input)?;
2796 Ok((input, trims))
2797}
2798
2799fn meter_type(input: &str) -> IResult<&str, MeterType> {
2805 alt((
2806 value(MeterType::Vu, alt((tag("vu"), tag("VU")))),
2807 value(MeterType::Ppm, alt((tag("ppm"), tag("PPM")))),
2808 value(MeterType::Peak, alt((tag("peak"), tag("PEAK")))),
2809 value(
2810 MeterType::GainReduction,
2811 alt((tag("gr"), tag("GR"), tag("gain_reduction"))),
2812 ),
2813 value(MeterType::TubeGlow, alt((tag("glow"), tag("tube_glow")))),
2814 value(MeterType::SupplySag, alt((tag("sag"), tag("supply_sag")))),
2815 ))(input)
2816}
2817
2818fn monitor_def(input: &str) -> IResult<&str, MonitorDef> {
2822 let (input, _) = ws_comments(input)?;
2823 let (input, component) = identifier(input)?;
2824 let (input, _) = ws_comments(input)?;
2825
2826 let (input, property) = opt(preceded(char('.'), identifier))(input)?;
2828 let property = property.unwrap_or("level").to_string();
2829
2830 let (input, _) = ws_comments(input)?;
2831 let (input, _) = tag("->")(input)?;
2832 let (input, _) = ws_comments(input)?;
2833
2834 let (input, label) = quoted_string(input)?;
2836 let (input, _) = ws_comments(input)?;
2837
2838 let (input, _) = char('[')(input)?;
2840 let (input, _) = ws_comments(input)?;
2841 let (input, mtype) = meter_type(input)?;
2842 let (input, _) = ws_comments(input)?;
2843 let (input, _) = char(']')(input)?;
2844
2845 Ok((
2846 input,
2847 MonitorDef {
2848 component: component.to_string(),
2849 property,
2850 label: label.to_string(),
2851 meter_type: mtype,
2852 },
2853 ))
2854}
2855
2856fn monitor_or_skip(input: &str) -> IResult<&str, Option<MonitorDef>> {
2858 let (input, _) = ws_comments(input)?;
2859
2860 if input.starts_with('}') {
2862 return Err(nom::Err::Error(nom::error::Error::new(
2863 input,
2864 nom::error::ErrorKind::Char,
2865 )));
2866 }
2867
2868 if let Ok((remaining, mon)) = monitor_def(input) {
2870 return Ok((remaining, Some(mon)));
2871 }
2872
2873 let (remaining, _) = skip_line(input)?;
2875 Ok((remaining, None))
2876}
2877
2878fn monitors_section(input: &str) -> IResult<&str, Vec<MonitorDef>> {
2880 let (input, _) = ws_comments(input)?;
2881 let (input, _) = tag("monitors")(input)?;
2882 let (input, _) = ws_comments(input)?;
2883 let (input, _) = char('{')(input)?;
2884
2885 let (input, maybe_mons) = many0(monitor_or_skip)(input)?;
2887 let mons: Vec<MonitorDef> = maybe_mons.into_iter().flatten().collect();
2888
2889 let (input, _) = ws_comments(input)?;
2890 let (input, _) = char('}')(input)?;
2891 Ok((input, mons))
2892}
2893
2894fn sidechain_field<'a>(input: &'a str, key: &str) -> IResult<&'a str, &'a str> {
2900 let (input, _) = ws_comments(input)?;
2901 let (input, _) = tag(key)(input)?;
2902 let (input, _) = ws_comments(input)?;
2903 let (input, _) = char(':')(input)?;
2904 let (input, _) = ws_comments(input)?;
2905 let (input, val) = take_while1(|c: char| c.is_alphanumeric() || c == '_')(input)?;
2906 Ok((input, val))
2907}
2908
2909fn sidechain_def(input: &str) -> IResult<&str, SidechainInfo> {
2911 let (input, _) = ws_comments(input)?;
2912 let (input, _) = tag("sidechain")(input)?;
2913 let (input, _) = ws_comments(input)?;
2914 let (input, _) = char('{')(input)?;
2915
2916 let mut tap_node = None;
2917 let mut cv_node = None;
2918 let mut target = None;
2919
2920 let mut input = input;
2921 loop {
2922 let (rest, _) = ws_comments(input)?;
2923 if let Ok((rest2, _)) = char::<&str, nom::error::Error<&str>>('}')(rest) {
2924 input = rest2;
2925 break;
2926 }
2927 if let Ok((rest2, val)) = sidechain_field(rest, "tap") {
2928 tap_node = Some(val.to_string());
2929 input = rest2;
2930 } else if let Ok((rest2, val)) = sidechain_field(rest, "cv") {
2931 cv_node = Some(val.to_string());
2932 input = rest2;
2933 } else if let Ok((rest2, val)) = sidechain_field(rest, "target") {
2934 target = Some(match val {
2935 "push_pull_grid" => SidechainTarget::PushPullGridBias,
2936 _ => SidechainTarget::PushPullGridBias, });
2938 input = rest2;
2939 } else {
2940 let (rest2, _) = take_till(|c| c == '\n' || c == '}')(rest)?;
2942 input = rest2;
2943 }
2944 }
2945
2946 let info = SidechainInfo {
2947 tap_node: tap_node.unwrap_or_default(),
2948 cv_node: cv_node.unwrap_or_default(),
2949 target: target.unwrap_or(SidechainTarget::PushPullGridBias),
2950 };
2951
2952 Ok((input, info))
2953}
2954
2955fn sidechains_section(input: &str) -> IResult<&str, Vec<SidechainInfo>> {
2957 let (input, _) = ws_comments(input)?;
2958 let (input, _) = tag("sidechains")(input)?;
2959 let (input, _) = ws_comments(input)?;
2960 let (input, _) = char('{')(input)?;
2961
2962 let (input, sidechains) = many0(sidechain_def)(input)?;
2963
2964 let (input, _) = ws_comments(input)?;
2965 let (input, _) = char('}')(input)?;
2966 Ok((input, sidechains))
2967}
2968
2969fn supply_section(input: &str) -> IResult<&str, SupplyConfig> {
2985 let (input, _) = ws_comments(input)?;
2986 let (input, _) = tag("supply")(input)?;
2987 let (input, _) = ws_comments(input)?;
2988 let (input, voltage) = recognize(pair(double, opt(alt((char('V'), char('v'))))))(input)?;
2991 let num_str = voltage.trim_end_matches(['V', 'v']);
2993 let volts = num_str.parse::<f64>().unwrap_or(9.0);
2994
2995 let (input, block) = opt(supply_block)(input)?;
2997
2998 let config = if let Some((impedance, filter_cap, rectifier)) = block {
2999 SupplyConfig {
3000 voltage: volts,
3001 impedance,
3002 filter_cap,
3003 rectifier: rectifier.unwrap_or_default(),
3004 }
3005 } else {
3006 SupplyConfig::voltage_only(volts)
3007 };
3008
3009 Ok((input, config))
3010}
3011
3012fn supply_block(input: &str) -> IResult<&str, (Option<f64>, Option<f64>, Option<RectifierType>)> {
3014 let (input, _) = ws_comments(input)?;
3015 let (input, _) = char('{')(input)?;
3016
3017 let mut impedance = None;
3018 let mut filter_cap = None;
3019 let mut rectifier = None;
3020
3021 let mut input = input;
3022 loop {
3023 let (rest, _) = ws_comments(input)?;
3024 let rest = if let Ok((r, _)) = char::<&str, nom::error::Error<&str>>(',')(rest) {
3026 let (r, _) = ws_comments(r)?;
3027 r
3028 } else {
3029 rest
3030 };
3031 if let Ok((rest2, _)) = char::<&str, nom::error::Error<&str>>('}')(rest) {
3033 input = rest2;
3034 break;
3035 }
3036 if let Ok((rest2, val)) = supply_field_impedance(rest) {
3038 impedance = Some(val);
3039 input = rest2;
3040 } else if let Ok((rest2, val)) = supply_field_filter_cap(rest) {
3041 filter_cap = Some(val);
3042 input = rest2;
3043 } else if let Ok((rest2, val)) = supply_field_rectifier(rest) {
3044 rectifier = Some(val);
3045 input = rest2;
3046 } else {
3047 let (rest2, _) = take_till(|c| c == ',' || c == '\n' || c == '}')(rest)?;
3049 input = rest2;
3050 }
3051 }
3052
3053 Ok((input, (impedance, filter_cap, rectifier)))
3054}
3055
3056fn supply_field_impedance(input: &str) -> IResult<&str, f64> {
3058 let (input, _) = tag("impedance")(input)?;
3059 let (input, _) = ws_comments(input)?;
3060 let (input, _) = char(':')(input)?;
3061 let (input, _) = ws_comments(input)?;
3062 let (input, val) = eng_value(input)?;
3063 let (input, _) = opt(preceded(ws_comments, preceded(char('#'), not_line_ending)))(input)?;
3065 Ok((input, val))
3066}
3067
3068fn supply_field_filter_cap(input: &str) -> IResult<&str, f64> {
3070 let (input, _) = tag("filter_cap")(input)?;
3071 let (input, _) = ws_comments(input)?;
3072 let (input, _) = char(':')(input)?;
3073 let (input, _) = ws_comments(input)?;
3074 let (input, val) = eng_value(input)?;
3075 let (input, _) = opt(preceded(ws_comments, preceded(char('#'), not_line_ending)))(input)?;
3077 Ok((input, val))
3078}
3079
3080fn supply_field_rectifier(input: &str) -> IResult<&str, RectifierType> {
3082 let (input, _) = tag("rectifier")(input)?;
3083 let (input, _) = ws_comments(input)?;
3084 let (input, _) = char(':')(input)?;
3085 let (input, _) = ws_comments(input)?;
3086 let (input, rtype) = alt((
3087 value(RectifierType::Tube, tag("tube")),
3088 value(RectifierType::SolidState, tag("solid_state")),
3089 ))(input)?;
3090 let (input, _) = opt(preceded(ws_comments, preceded(char('#'), not_line_ending)))(input)?;
3092 Ok((input, rtype))
3093}
3094
3095fn named_supply(input: &str) -> IResult<&str, NamedSupply> {
3102 let (input, _) = ws_comments(input)?;
3103 let (input, name) = identifier_or_supply_rail(input)?;
3105 let (input, _) = ws_comments(input)?;
3106 let (input, _) = char(':')(input)?;
3107 let (input, _) = ws_comments(input)?;
3108
3109 let (input, is_negative) = opt(char('-'))(input)?;
3111
3112 let (input, voltage_str) = recognize(pair(double, opt(alt((char('V'), char('v'))))))(input)?;
3114 let num_str = voltage_str.trim_end_matches(['V', 'v']);
3115 let mut volts = num_str.parse::<f64>().unwrap_or(9.0);
3116 if is_negative.is_some() {
3117 volts = -volts;
3118 }
3119
3120 let (input, block) = opt(supply_block)(input)?;
3122
3123 let config = if let Some((impedance, filter_cap, rectifier)) = block {
3124 SupplyConfig {
3125 voltage: volts,
3126 impedance,
3127 filter_cap,
3128 rectifier: rectifier.unwrap_or_default(),
3129 }
3130 } else {
3131 SupplyConfig::voltage_only(volts)
3132 };
3133
3134 Ok((
3135 input,
3136 NamedSupply {
3137 name: name.to_string(),
3138 config,
3139 },
3140 ))
3141}
3142
3143fn supplies_section(input: &str) -> IResult<&str, Vec<NamedSupply>> {
3154 let (input, _) = ws_comments(input)?;
3155 let (input, _) = tag("supplies")(input)?;
3156 let (input, _) = ws_comments(input)?;
3157 let (input, _) = char('{')(input)?;
3158
3159 let (input, supplies) = many0(named_supply)(input)?;
3160
3161 let (input, _) = ws_comments(input)?;
3162 let (input, _) = char('}')(input)?;
3163
3164 Ok((input, supplies))
3165}
3166
3167fn parse_rate(input: &str) -> IResult<&str, u32> {
3173 let (input, _) = ws_comments(input)?;
3174 let (input, _) = tag("rate")(input)?;
3175 let (input, _) = ws_comments(input)?;
3176 let (input, _) = char(':')(input)?;
3177 let (input, _) = ws_comments(input)?;
3178 let (input, _) = tag("1")(input)?;
3179 let (input, _) = ws_comments(input)?;
3180 let (input, _) = char('/')(input)?;
3181 let (input, _) = ws_comments(input)?;
3182 let (input, divisor) = nom::character::complete::u32(input)?;
3183 if divisor == 0 || (divisor & (divisor - 1)) != 0 {
3185 return Err(nom::Err::Failure(nom::error::Error::new(
3186 input,
3187 nom::error::ErrorKind::Verify,
3188 )));
3189 }
3190 Ok((input, divisor))
3191}
3192
3193fn subcircuit_block(input: &str) -> IResult<&str, SubcircuitDef> {
3195 let (input, _) = ws_comments(input)?;
3196 let (input, _) = tag("subcircuit")(input)?;
3197 let (input, _) = ws_comments(input)?;
3198 let (input, name) = identifier(input)?;
3199 let (input, _) = ws_comments(input)?;
3200 let (input, _) = char('{')(input)?;
3201
3202 let (input, rate) = opt(parse_rate)(input)?;
3204
3205 let (input, (components, mirrors, _uses)) = components_section(input)?;
3209 let (input, nets) = nets_section(input)?;
3210 let (input, controls) = opt(controls_section)(input)?;
3211 let (input, trims) = opt(trims_section)(input)?;
3212 let (input, monitors) = opt(monitors_section)(input)?;
3213
3214 let (input, _) = ws_comments(input)?;
3215 let (input, _) = char('}')(input)?;
3216
3217 Ok((
3218 input,
3219 SubcircuitDef {
3220 name: name.to_string(),
3221 rate,
3222 components,
3223 nets,
3224 controls: controls.unwrap_or_default(),
3225 trims: trims.unwrap_or_default(),
3226 monitors: monitors.unwrap_or_default(),
3227 mirrors,
3228 },
3229 ))
3230}
3231
3232fn resolve_subcircuit_pins(pedal: &mut PedalDef) {
3236 use std::collections::HashSet;
3237 let mut sc_names: HashSet<&str> = pedal.subcircuits.iter().map(|s| s.name.as_str()).collect();
3242 for u in &pedal.uses {
3243 sc_names.insert(u.id.as_str());
3244 }
3245 if sc_names.is_empty() {
3246 return;
3247 }
3248
3249 fn resolve_pin(pin: &Pin, sc_names: &std::collections::HashSet<&str>) -> Pin {
3250 match pin {
3251 Pin::ComponentPin {
3252 component,
3253 pin: port,
3254 } if sc_names.contains(component.as_str()) => Pin::SubcircuitPort {
3255 subcircuit: component.clone(),
3256 port: port.clone(),
3257 },
3258 Pin::Fork {
3259 switch,
3260 destinations,
3261 } => Pin::Fork {
3262 switch: switch.clone(),
3263 destinations: destinations
3264 .iter()
3265 .map(|d| resolve_pin(d, sc_names))
3266 .collect(),
3267 },
3268 other => other.clone(),
3269 }
3270 }
3271
3272 for net in &mut pedal.nets {
3273 net.from = resolve_pin(&net.from, &sc_names);
3274 net.to = net.to.iter().map(|p| resolve_pin(p, &sc_names)).collect();
3275 }
3276}
3277
3278fn parse_subtitle(input: &str) -> IResult<&str, String> {
3279 let (input, _) = tag("subtitle")(input)?;
3280 let (input, _) = ws_comments(input)?;
3281 let (input, s) = quoted_string(input)?;
3282 let (input, _) = ws_comments(input)?;
3283 Ok((input, s.to_string()))
3284}
3285
3286fn parse_calibrate(input: &str) -> IResult<&str, ()> {
3288 let (input, _) = ws_comments(input)?;
3289 let (input, _) = tag("calibrate")(input)?;
3290 let (input, _) = ws_comments(input)?;
3291 Ok((input, ()))
3292}
3293
3294const VALID_INIT_STATES: &[&str] = &["saturated", "cutoff", "active", "forward", "reverse"];
3300
3301fn parse_init_hint(input: &str) -> IResult<&str, InitHint> {
3303 let (input, _) = ws_comments(input)?;
3304 let (input, label) = identifier(input)?;
3305 let (input, _) = ws_comments(input)?;
3306 let (input, _) = char(':')(input)?;
3307 let (input, _) = ws_comments(input)?;
3308 let (input, state_str) = identifier(input)?;
3309 let (input, _) = ws_comments(input)?;
3310
3311 if !VALID_INIT_STATES.contains(&state_str) {
3312 return Err(nom::Err::Failure(nom::error::Error::new(
3313 input,
3314 nom::error::ErrorKind::Tag,
3315 )));
3316 }
3317
3318 Ok((
3319 input,
3320 InitHint {
3321 device_label: label.to_string(),
3322 state: InitState::Named(state_str.to_string()),
3323 },
3324 ))
3325}
3326
3327fn init_section(input: &str) -> IResult<&str, Vec<InitHint>> {
3336 let (input, _) = ws_comments(input)?;
3337 let (input, _) = tag("init")(input)?;
3338 let (input, _) = ws_comments(input)?;
3339 let (input, _) = char('{')(input)?;
3340 let (input, hints) = many0(parse_init_hint)(input)?;
3341 let (input, _) = ws_comments(input)?;
3342 let (input, _) = char('}')(input)?;
3343 Ok((input, hints))
3344}
3345
3346fn parse_op_entry(input: &str) -> IResult<&str, InitHint> {
3356 let (input, _) = ws_comments(input)?;
3357 let (input, label) = identifier(input)?;
3358 let (input, _) = ws_comments(input)?;
3359 let (input, _) = char(':')(input)?;
3360 let (input, _) = ws_comments(input)?;
3361 let (input, _) = char('{')(input)?;
3362
3363 let mut vbe: Option<f64> = None;
3364 let mut vce: Option<f64> = None;
3365 let mut rest = input;
3366 loop {
3367 let (r, _) = ws_comments(rest)?;
3368 if let Ok((r2, _)) = char::<&str, nom::error::Error<&str>>('}')(r) {
3370 rest = r2;
3371 break;
3372 }
3373 let (r, field) = identifier(r)?;
3374 let (r, _) = ws_comments(r)?;
3375 let (r, _) = char(':')(r)?;
3376 let (r, _) = ws_comments(r)?;
3377 let (r, val) = double(r)?;
3378 match field {
3379 "vbe" => vbe = Some(val),
3380 "vce" => vce = Some(val),
3381 _ => {
3382 return Err(nom::Err::Failure(nom::error::Error::new(
3383 r,
3384 nom::error::ErrorKind::Tag,
3385 )));
3386 }
3387 }
3388 let (r, _) = ws_comments(r)?;
3390 let (r, _) = opt(char(','))(r)?;
3391 rest = r;
3392 }
3393 let (input, _) = ws_comments(rest)?;
3394 let (input, _) = opt(char(','))(input)?;
3396
3397 let (vbe, vce) = match (vbe, vce) {
3398 (Some(b), Some(c)) => (b, c),
3399 _ => {
3400 return Err(nom::Err::Failure(nom::error::Error::new(
3401 input,
3402 nom::error::ErrorKind::Tag,
3403 )));
3404 }
3405 };
3406
3407 Ok((
3408 input,
3409 InitHint {
3410 device_label: label.to_string(),
3411 state: InitState::Explicit { vbe, vce },
3412 },
3413 ))
3414}
3415
3416fn op_section(input: &str) -> IResult<&str, Vec<InitHint>> {
3426 let (input, _) = ws_comments(input)?;
3427 let (input, _) = tag("op")(input)?;
3428 let (input, _) = ws_comments(input)?;
3429 let (input, _) = char('{')(input)?;
3430 let (input, hints) = many0(parse_op_entry)(input)?;
3431 let (input, node_hints) = opt(op_nodes_block)(input)?;
3435 let (input, _) = ws_comments(input)?;
3436 let (input, _) = char('}')(input)?;
3437 let mut hints = hints;
3438 if let Some(mut nh) = node_hints {
3439 hints.append(&mut nh);
3440 }
3441 Ok((input, hints))
3442}
3443
3444fn op_nodes_block(input: &str) -> IResult<&str, Vec<InitHint>> {
3456 let (input, _) = ws_comments(input)?;
3457 let (input, _) = tag("nodes")(input)?;
3458 let (input, _) = ws_comments(input)?;
3459 let (input, _) = char('{')(input)?;
3460 let mut hints = Vec::new();
3461 let mut rest = input;
3462 loop {
3463 let (r, _) = ws_comments(rest)?;
3464 if let Ok((r2, _)) = char::<&str, nom::error::Error<&str>>('}')(r) {
3465 rest = r2;
3466 break;
3467 }
3468 let (r, head) = identifier(r)?;
3471 let (r, tail) = opt(|i| {
3472 let (i, _) = char('.')(i)?;
3473 identifier(i)
3474 })(r)?;
3475 let name = match tail {
3476 Some(pin) => format!("{head}.{pin}"),
3477 None => head.to_string(),
3478 };
3479 let (r, _) = ws_comments(r)?;
3480 let (r, _) = char(':')(r)?;
3481 let (r, _) = ws_comments(r)?;
3482 let (r, val) = double(r)?;
3483 hints.push(InitHint {
3484 device_label: name,
3485 state: InitState::NodeVoltage { v: val },
3486 });
3487 let (r, _) = ws_comments(r)?;
3488 let (r, _) = opt(char(','))(r)?;
3489 rest = r;
3490 }
3491 Ok((rest, hints))
3492}
3493
3494pub fn parse_pedal(input: &str) -> IResult<&str, PedalDef> {
3497 let (input, _) = ws_comments(input)?;
3498 let (input, _) = alt((tag("pedal"), tag("synth"), tag("equipment")))(input)?;
3500 let (input, _) = ws_comments(input)?;
3501 let (input, name) = quoted_string(input)?;
3502 let (input, _) = ws_comments(input)?;
3503
3504 let (input, subtitle) = opt(parse_subtitle)(input)?;
3507
3508 let (input, _) = char('{')(input)?;
3509
3510 let (input, supplies) = if let Ok((rest, multi)) = supplies_section(input) {
3514 (rest, multi)
3515 } else if let Ok((rest, single)) = supply_section(input) {
3516 (rest, vec![NamedSupply::with_config("vcc", single)])
3518 } else {
3519 (input, Vec::new())
3520 };
3521
3522 let (input, subcircuits) = many0(subcircuit_block)(input)?;
3524
3525 let (input, ports) = opt(ports_section)(input)?;
3528
3529 let (input, (components, mirrors, uses)) = if subcircuits.is_empty() {
3532 components_section(input)?
3533 } else if let Ok((rest, result)) = components_section(input) {
3534 (rest, result)
3535 } else {
3536 (input, (Vec::new(), hashbrown::HashMap::new(), Vec::new()))
3537 };
3538 let (input, nets) = if subcircuits.is_empty() {
3539 nets_section(input)?
3540 } else if let Ok((rest, result)) = nets_section(input) {
3541 (rest, result)
3542 } else {
3543 (input, Vec::new())
3544 };
3545 let (input, controls) = opt(controls_section)(input)?;
3546 let (input, trims) = opt(trims_section)(input)?;
3547 let (input, monitors) = opt(monitors_section)(input)?;
3548 let (input, sidechains) = opt(sidechains_section)(input)?;
3549 let (input, calibrate) = opt(parse_calibrate)(input)?;
3550 let (input, init_hints) = opt(init_section)(input)?;
3551 let (input, op_hints) = opt(op_section)(input)?;
3555
3556 let (input, _) = ws_comments(input)?;
3557 let (input, _) = char('}')(input)?;
3558 let (input, _) = ws_comments(input)?;
3559
3560 let mut pedal = PedalDef {
3561 name: name.to_string(),
3562 subtitle,
3563 supplies,
3564 components,
3565 nets,
3566 controls: controls.unwrap_or_default(),
3567 trims: trims.unwrap_or_default(),
3568 monitors: monitors.unwrap_or_default(),
3569 sidechains: sidechains.unwrap_or_default(),
3570 mirrors,
3571 calibrate: calibrate.is_some(),
3572 subcircuits,
3573 ports: ports.unwrap_or_default(),
3574 init_hints: {
3575 let mut h = init_hints.unwrap_or_default();
3576 h.extend(op_hints.unwrap_or_default());
3577 h
3578 },
3579 uses,
3580 };
3581 resolve_subcircuit_pins(&mut pedal);
3582 Ok((input, pedal))
3583}
3584
3585pub fn parse_pedal_file(src: &str) -> Result<PedalDef, String> {
3587 match parse_pedal(src) {
3588 Ok(("", def)) => Ok(def),
3589 Ok((rest, _)) => Err(format!("Trailing input: {:?}", &rest[..rest.len().min(60)])),
3590 Err(e) => Err(format!("Parse error: {e}")),
3591 }
3592}
3593
3594#[cfg(test)]
3599mod tests {
3600 use super::*;
3601 use crate::compiler::component::Component;
3602 use crate::compiler::components::*;
3603
3604 #[test]
3605 fn parse_eng_value_k() {
3606 let (_, v) = eng_value("4.7k").unwrap();
3607 assert!((v - 4700.0).abs() < 1e-6);
3608 }
3609
3610 #[test]
3611 fn parse_eng_value_n() {
3612 let (_, v) = eng_value("220n").unwrap();
3613 assert!((v - 220e-9).abs() < 1e-18);
3614 }
3615
3616 #[test]
3617 fn parse_eng_value_m() {
3618 let (_, v) = eng_value("100m").unwrap();
3619 assert!((v - 0.1).abs() < 1e-10);
3620 }
3621
3622 #[test]
3623 #[allow(non_snake_case)]
3624 fn parse_eng_value_M() {
3625 let (_, v) = eng_value("1M").unwrap();
3626 assert!((v - 1e6).abs() < 1e-2);
3627 }
3628
3629 #[test]
3630 fn parse_eng_value_inf() {
3631 let (_, v) = eng_value("inf").unwrap();
3632 assert!(v.is_infinite() && v.is_sign_positive());
3633 }
3634
3635 #[test]
3636 fn parse_resistor_switched_with_inf() {
3637 let (_, (c, _)) = component_def("R_sel: resistor_switched(10k, inf, 47k)").unwrap();
3638 assert_eq!(c.id, "R_sel");
3639 let rs = c
3640 .kind
3641 .as_any()
3642 .downcast_ref::<ResistorSwitched>()
3643 .expect("expected ResistorSwitched");
3644 assert_eq!(rs.values.len(), 3);
3645 assert!((rs.values[0] - 10_000.0).abs() < 1e-6);
3646 assert!(rs.values[1].is_infinite());
3647 assert!((rs.values[2] - 47_000.0).abs() < 1e-6);
3648 }
3649
3650 #[test]
3651 fn parse_resistor_switched_inf_only() {
3652 let (_, (c, _)) = component_def("R_open: resistor_switched([inf])").unwrap();
3654 let rs = c
3655 .kind
3656 .as_any()
3657 .downcast_ref::<ResistorSwitched>()
3658 .expect("expected ResistorSwitched");
3659 assert_eq!(rs.values.len(), 1);
3660 assert!(rs.values[0].is_infinite());
3661 }
3662
3663 #[test]
3664 fn parse_component_resistor() {
3665 let (_, (c, _)) = component_def("R1: resistor(4.7k)").unwrap();
3666 assert_eq!(c.id, "R1");
3667 assert_eq!(
3668 c.kind.as_any().downcast_ref::<Resistor>().unwrap(),
3669 &Resistor { value: 4700.0 }
3670 );
3671 }
3672
3673 #[test]
3674 fn parse_component_cap() {
3675 let (_, (c, _)) = component_def("C1: cap(220n)").unwrap();
3676 assert_eq!(c.id, "C1");
3677 let cap = c
3678 .kind
3679 .as_any()
3680 .downcast_ref::<Capacitor>()
3681 .expect("expected Capacitor");
3682 assert!((cap.config.value - 220e-9).abs() < 1e-18);
3683 assert_eq!(cap.config.cap_type, CapType::Film); assert!(cap.config.leakage.is_none());
3685 assert!(cap.config.da.is_none());
3686 }
3687
3688 #[test]
3689 fn parse_component_cap_electrolytic() {
3690 let (_, (c, _)) = component_def("C1: cap(22u, electrolytic)").unwrap();
3691 let cap = c
3692 .kind
3693 .as_any()
3694 .downcast_ref::<Capacitor>()
3695 .expect("expected Capacitor");
3696 assert!((cap.config.value - 22e-6).abs() < 1e-15);
3697 assert_eq!(cap.config.cap_type, CapType::Electrolytic);
3698 assert!(cap.config.leakage.is_none());
3699 assert!(cap.config.da.is_none());
3700 }
3701
3702 #[test]
3703 fn parse_component_cap_with_leakage() {
3704 let (_, (c, _)) = component_def("C1: cap(22u, electrolytic, leakage: 100k)").unwrap();
3705 let cap = c
3706 .kind
3707 .as_any()
3708 .downcast_ref::<Capacitor>()
3709 .expect("expected Capacitor");
3710 assert!((cap.config.value - 22e-6).abs() < 1e-15);
3711 assert_eq!(cap.config.cap_type, CapType::Electrolytic);
3712 assert!((cap.config.leakage.unwrap() - 100_000.0).abs() < 1.0);
3713 assert!(cap.config.da.is_none());
3714 }
3715
3716 #[test]
3717 fn parse_component_cap_with_da() {
3718 let (_, (c, _)) =
3719 component_def("C1: cap(22u, electrolytic, leakage: 10k, da: 0.05)").unwrap();
3720 let cap = c
3721 .kind
3722 .as_any()
3723 .downcast_ref::<Capacitor>()
3724 .expect("expected Capacitor");
3725 assert!((cap.config.value - 22e-6).abs() < 1e-15);
3726 assert_eq!(cap.config.cap_type, CapType::Electrolytic);
3727 assert!((cap.config.leakage.unwrap() - 10_000.0).abs() < 1.0);
3728 assert!((cap.config.da.unwrap() - 0.05).abs() < 0.001);
3729 }
3730
3731 #[test]
3732 fn parse_component_diode_pair() {
3733 let (_, (c, _)) = component_def("D1: diode_pair(silicon)").unwrap();
3734 assert_eq!(
3735 c.kind.as_any().downcast_ref::<DiodePair>().unwrap(),
3736 &DiodePair {
3737 diode_type: DiodeType::Silicon
3738 }
3739 );
3740 }
3741
3742 #[test]
3743 fn parse_component_pot() {
3744 let (_, (c, _)) = component_def("Gain: pot(500k)").unwrap();
3745 assert_eq!(
3746 c.kind.as_any().downcast_ref::<Potentiometer>().unwrap(),
3747 &Potentiometer {
3748 max_r: 500_000.0,
3749 taper: PotTaper::B
3750 }
3751 );
3752 }
3753
3754 #[test]
3755 fn parse_component_pot_with_taper() {
3756 let (_, (c, _)) = component_def("Vol: pot(100k, a)").unwrap();
3758 assert_eq!(
3759 c.kind.as_any().downcast_ref::<Potentiometer>().unwrap(),
3760 &Potentiometer {
3761 max_r: 100_000.0,
3762 taper: PotTaper::A
3763 }
3764 );
3765
3766 let (_, (c, _)) = component_def("Tone: pot(10k, b)").unwrap();
3768 assert_eq!(
3769 c.kind.as_any().downcast_ref::<Potentiometer>().unwrap(),
3770 &Potentiometer {
3771 max_r: 10_000.0,
3772 taper: PotTaper::B
3773 }
3774 );
3775
3776 let (_, (c, _)) = component_def("Mix: pot(50k, c)").unwrap();
3778 assert_eq!(
3779 c.kind.as_any().downcast_ref::<Potentiometer>().unwrap(),
3780 &Potentiometer {
3781 max_r: 50_000.0,
3782 taper: PotTaper::C
3783 }
3784 );
3785
3786 let (_, (c, _)) = component_def("P1: pot(1M, log)").unwrap();
3788 assert_eq!(
3789 c.kind.as_any().downcast_ref::<Potentiometer>().unwrap(),
3790 &Potentiometer {
3791 max_r: 1_000_000.0,
3792 taper: PotTaper::A
3793 }
3794 );
3795
3796 let (_, (c, _)) = component_def("P2: pot(25k, linear)").unwrap();
3797 assert_eq!(
3798 c.kind.as_any().downcast_ref::<Potentiometer>().unwrap(),
3799 &Potentiometer {
3800 max_r: 25_000.0,
3801 taper: PotTaper::B
3802 }
3803 );
3804 }
3805
3806 #[test]
3807 fn parse_component_pot_mirrors() {
3808 let (_, (c, m)) = component_def("Gain_B: pot(100k, b) mirrors Gain_A").unwrap();
3809 assert_eq!(c.id, "Gain_B");
3810 assert_eq!(
3811 c.kind.as_any().downcast_ref::<Potentiometer>().unwrap(),
3812 &Potentiometer {
3813 max_r: 100_000.0,
3814 taper: PotTaper::B
3815 }
3816 );
3817 assert_eq!(m, Some("Gain_A".to_string()));
3818 }
3819
3820 #[test]
3821 fn parse_component_pot_no_mirrors() {
3822 let (_, (c, m)) = component_def("Gain_A: pot(100k, b)").unwrap();
3823 assert_eq!(c.id, "Gain_A");
3824 assert_eq!(m, None);
3825 }
3826
3827 #[test]
3828 fn parse_mirrors_in_full_pedal() {
3829 let src = r#"
3830 pedal "Dual Gang Test" {
3831 components {
3832 Gain_A: pot(100k, b)
3833 Gain_B: pot(100k, b) mirrors Gain_A
3834 R1: resistor(10k)
3835 }
3836 nets {
3837 in -> R1.a
3838 R1.b -> Gain_A.a
3839 Gain_A.b -> Gain_B.a
3840 Gain_B.b -> out
3841 }
3842 }
3843 "#;
3844 let pedal = parse_pedal_file(src).unwrap();
3845 assert_eq!(pedal.mirrors.len(), 1);
3846 assert_eq!(pedal.mirrors.get("Gain_B").unwrap(), "Gain_A");
3847 }
3848
3849 #[test]
3850 fn parse_calibrate_keyword() {
3851 let src = r#"
3852 pedal "Test" {
3853 components {
3854 R1: resistor(10k)
3855 }
3856 nets {
3857 in -> R1.a
3858 R1.b -> out
3859 }
3860 calibrate
3861 }
3862 "#;
3863 let pedal = parse_pedal_file(src).unwrap();
3864 assert!(pedal.calibrate);
3865 }
3866
3867 #[test]
3868 fn parse_no_calibrate_keyword() {
3869 let src = r#"
3870 pedal "Test" {
3871 components {
3872 R1: resistor(10k)
3873 }
3874 nets {
3875 in -> R1.a
3876 R1.b -> out
3877 }
3878 }
3879 "#;
3880 let pedal = parse_pedal_file(src).unwrap();
3881 assert!(!pedal.calibrate);
3882 }
3883
3884 #[test]
3885 fn pot_taper_curves() {
3886 assert!((PotTaper::B.apply(0.0) - 0.0).abs() < 1e-9);
3888 assert!((PotTaper::B.apply(0.5) - 0.5).abs() < 1e-9);
3889 assert!((PotTaper::B.apply(1.0) - 1.0).abs() < 1e-9);
3890
3891 let a_mid = PotTaper::A.apply(0.5);
3894 assert!(a_mid > 0.2 && a_mid < 0.3, "A taper at 50% should be ~24%");
3895 assert!((PotTaper::A.apply(0.0) - 0.0).abs() < 1e-9);
3896 assert!((PotTaper::A.apply(1.0) - 1.0).abs() < 1e-9);
3897
3898 let c_mid = PotTaper::C.apply(0.5);
3900 assert!(c_mid > 0.7 && c_mid < 0.8, "C taper at 50% should be ~76%");
3901 assert!((PotTaper::C.apply(0.0) - 0.0).abs() < 1e-9);
3902 assert!((PotTaper::C.apply(1.0) - 1.0).abs() < 1e-9);
3903
3904 let a_25 = PotTaper::A.apply(0.25);
3906 let c_75 = PotTaper::C.apply(0.75);
3907 assert!(
3908 (a_25 - (1.0 - c_75)).abs() < 0.01,
3909 "A and C should be symmetric"
3910 );
3911 }
3912
3913 #[test]
3914 fn parse_net_simple() {
3915 let (_, n) = net_def("in -> C1.a").unwrap();
3916 assert_eq!(n.from, Pin::Reserved("in".to_string()));
3917 assert_eq!(
3918 n.to,
3919 vec![Pin::ComponentPin {
3920 component: "C1".to_string(),
3921 pin: "a".to_string()
3922 }]
3923 );
3924 }
3925
3926 #[test]
3927 fn parse_net_multi() {
3928 let (_, n) = net_def("C1.b -> R1.a, D1.a").unwrap();
3929 assert_eq!(n.to.len(), 2);
3930 }
3931
3932 #[test]
3933 fn parse_net_fork_simple() {
3934 let (_, n) = net_def("B7.a -> fork(SW_time, [B8.a, B9.a])").unwrap();
3935 assert_eq!(
3936 n.from,
3937 Pin::ComponentPin {
3938 component: "B7".to_string(),
3939 pin: "a".to_string()
3940 }
3941 );
3942 assert_eq!(n.to.len(), 1);
3943 match &n.to[0] {
3944 Pin::Fork {
3945 switch,
3946 destinations,
3947 } => {
3948 assert_eq!(switch, "SW_time");
3949 assert_eq!(destinations.len(), 2);
3950 assert_eq!(
3951 destinations[0],
3952 Pin::ComponentPin {
3953 component: "B8".to_string(),
3954 pin: "a".to_string()
3955 }
3956 );
3957 assert_eq!(
3958 destinations[1],
3959 Pin::ComponentPin {
3960 component: "B9".to_string(),
3961 pin: "a".to_string()
3962 }
3963 );
3964 }
3965 _ => panic!("expected Fork"),
3966 }
3967 }
3968
3969 #[test]
3970 fn parse_net_fork_three_destinations() {
3971 let (_, n) = net_def("in -> fork(MODE, [clean.a, crunch.a, lead.a])").unwrap();
3972 assert_eq!(n.from, Pin::Reserved("in".to_string()));
3973 match &n.to[0] {
3974 Pin::Fork {
3975 switch,
3976 destinations,
3977 } => {
3978 assert_eq!(switch, "MODE");
3979 assert_eq!(destinations.len(), 3);
3980 }
3981 _ => panic!("expected Fork"),
3982 }
3983 }
3984
3985 #[test]
3986 fn parse_net_fork_with_reserved_dest() {
3987 let (_, n) = net_def("sig.out -> fork(MUTE, [out, gnd])").unwrap();
3989 match &n.to[0] {
3990 Pin::Fork {
3991 switch,
3992 destinations,
3993 } => {
3994 assert_eq!(switch, "MUTE");
3995 assert_eq!(destinations[0], Pin::Reserved("out".to_string()));
3996 assert_eq!(destinations[1], Pin::Reserved("gnd".to_string()));
3997 }
3998 _ => panic!("expected Fork"),
3999 }
4000 }
4001
4002 #[test]
4003 fn parse_control() {
4004 let (_, c) = control_def(r#"Gain.position -> "Drive" [0.0, 1.0] = 0.5"#).unwrap();
4005 assert_eq!(c.component, "Gain");
4006 assert_eq!(c.label, "Drive");
4007 assert!((c.default - 0.5).abs() < 1e-10);
4008 }
4009
4010 #[test]
4011 fn parse_full_pedal() {
4012 let src = r#"
4013pedal "Tube Screamer" {
4014 components {
4015 R1: resistor(4.7k)
4016 C1: cap(220n)
4017 D1: diode_pair(silicon)
4018 Gain: pot(500k)
4019 }
4020 nets {
4021 in -> C1.a
4022 C1.b -> R1.a, D1.a
4023 D1.b -> gnd
4024 }
4025 controls {
4026 Gain.position -> "Drive" [0.0, 1.0] = 0.5
4027 }
4028}
4029"#;
4030 let def = parse_pedal_file(src).unwrap();
4031 assert_eq!(def.name, "Tube Screamer");
4032 assert_eq!(def.components.len(), 4);
4033 assert_eq!(def.nets.len(), 3);
4034 assert_eq!(def.controls.len(), 1);
4035 }
4036
4037 #[test]
4038 fn parse_with_comments() {
4039 let src = r#"
4040# This is a fuzz pedal
4041pedal "Fuzz" {
4042 components {
4043 # Input cap
4044 C1: cap(100n)
4045 Q1: npn()
4046 }
4047 nets {
4048 in -> C1.a
4049 C1.b -> Q1.base
4050 }
4051}
4052"#;
4053 let def = parse_pedal_file(src).unwrap();
4054 assert_eq!(def.name, "Fuzz");
4055 assert_eq!(def.components.len(), 2);
4056 }
4057
4058 #[test]
4059 fn parse_all_component_kinds() {
4060 let src = r#"
4061pedal "All" {
4062 components {
4063 R1: resistor(10k)
4064 C1: cap(100n)
4065 L1: inductor(100m)
4066 D1: diode_pair(germanium)
4067 D2: diode(led)
4068 P1: pot(1M)
4069 Q1: npn()
4070 Q2: pnp()
4071 U1: opamp()
4072 }
4073 nets {
4074 in -> out
4075 }
4076}
4077"#;
4078 let def = parse_pedal_file(src).unwrap();
4079 assert_eq!(def.components.len(), 9);
4080 }
4081
4082 #[test]
4083 fn parse_njfet_j201() {
4084 let (_, (c, _)) = component_def("J1: njfet(j201)").unwrap();
4085 assert_eq!(c.id, "J1");
4086 assert_eq!(
4087 c.kind.as_any().downcast_ref::<NJfet>().unwrap(),
4088 &NJfet {
4089 model: "J201".to_string()
4090 }
4091 );
4092 }
4093
4094 #[test]
4095 fn parse_njfet_2n5457() {
4096 let (_, (c, _)) = component_def("J2: njfet(2n5457)").unwrap();
4097 assert_eq!(
4098 c.kind.as_any().downcast_ref::<NJfet>().unwrap(),
4099 &NJfet {
4100 model: "2N5457".to_string()
4101 }
4102 );
4103 }
4104
4105 #[test]
4106 fn parse_njfet_2n5952() {
4107 let (_, (c, _)) = component_def("J1: njfet(2n5952)").unwrap();
4108 assert_eq!(
4109 c.kind.as_any().downcast_ref::<NJfet>().unwrap(),
4110 &NJfet {
4111 model: "2N5952".to_string()
4112 }
4113 );
4114 }
4115
4116 #[test]
4117 fn parse_pjfet_2n5460() {
4118 let (_, (c, _)) = component_def("J3: pjfet(2n5460)").unwrap();
4119 assert_eq!(
4120 c.kind.as_any().downcast_ref::<PJfet>().unwrap(),
4121 &PJfet {
4122 model: "2N5460".to_string()
4123 }
4124 );
4125 }
4126
4127 #[test]
4128 fn parse_njfet_any_model() {
4129 let (_, (c, _)) = component_def("J1: njfet(2N3819-VSH)").unwrap();
4131 assert_eq!(
4132 c.kind.as_any().downcast_ref::<NJfet>().unwrap(),
4133 &NJfet {
4134 model: "2N3819-VSH".to_string()
4135 }
4136 );
4137 }
4138
4139 #[test]
4140 fn parse_pedal_with_jfet() {
4141 let src = r#"
4142pedal "Tremolo" {
4143 components {
4144 C1: cap(100n)
4145 J1: njfet(j201)
4146 R1: resistor(10k)
4147 }
4148 nets {
4149 in -> C1.a
4150 C1.b -> J1.gate
4151 J1.drain -> R1.a
4152 J1.source -> gnd
4153 }
4154}
4155"#;
4156 let def = parse_pedal_file(src).unwrap();
4157 assert_eq!(def.name, "Tremolo");
4158 assert_eq!(def.components.len(), 3);
4159 assert_eq!(
4160 def.components[1]
4161 .kind
4162 .as_any()
4163 .downcast_ref::<NJfet>()
4164 .unwrap(),
4165 &NJfet {
4166 model: "J201".to_string()
4167 }
4168 );
4169 }
4170
4171 #[test]
4172 fn parse_photocoupler_vtl5c3() {
4173 let (_, (c, _)) = component_def("OC1: photocoupler(vtl5c3)").unwrap();
4174 assert_eq!(c.id, "OC1");
4175 assert_eq!(
4176 c.kind.as_any().downcast_ref::<PhotocouplerComp>().unwrap(),
4177 &PhotocouplerComp {
4178 coupler_type: PhotocouplerType::Vtl5c3
4179 }
4180 );
4181 }
4182
4183 #[test]
4184 fn parse_photocoupler_vtl5c1() {
4185 let (_, (c, _)) = component_def("OC2: photocoupler(vtl5c1)").unwrap();
4186 assert_eq!(
4187 c.kind.as_any().downcast_ref::<PhotocouplerComp>().unwrap(),
4188 &PhotocouplerComp {
4189 coupler_type: PhotocouplerType::Vtl5c1
4190 }
4191 );
4192 }
4193
4194 #[test]
4195 fn parse_photocoupler_nsl32() {
4196 let (_, (c, _)) = component_def("OC3: photocoupler(nsl32)").unwrap();
4197 assert_eq!(
4198 c.kind.as_any().downcast_ref::<PhotocouplerComp>().unwrap(),
4199 &PhotocouplerComp {
4200 coupler_type: PhotocouplerType::Nsl32
4201 }
4202 );
4203 }
4204
4205 #[test]
4206 fn parse_pedal_with_photocoupler() {
4207 let src = r#"
4208pedal "Optical Tremolo" {
4209 components {
4210 C1: cap(100n)
4211 OC1: photocoupler(vtl5c3)
4212 R1: resistor(10k)
4213 }
4214 nets {
4215 in -> C1.a
4216 C1.b -> OC1.a
4217 OC1.b -> R1.a
4218 R1.b -> out
4219 }
4220}
4221"#;
4222 let def = parse_pedal_file(src).unwrap();
4223 assert_eq!(def.name, "Optical Tremolo");
4224 assert_eq!(def.components.len(), 3);
4225 assert_eq!(
4226 def.components[1]
4227 .kind
4228 .as_any()
4229 .downcast_ref::<PhotocouplerComp>()
4230 .unwrap(),
4231 &PhotocouplerComp {
4232 coupler_type: PhotocouplerType::Vtl5c3
4233 }
4234 );
4235 }
4236
4237 #[test]
4238 fn parse_triode_12ax7() {
4239 let (_, (c, _)) = component_def("V1: triode(12ax7)").unwrap();
4240 assert_eq!(c.id, "V1");
4241 assert_eq!(
4242 c.kind.as_any().downcast_ref::<Triode>().unwrap(),
4243 &Triode {
4244 model: "12AX7".into()
4245 }
4246 );
4247 }
4248
4249 #[test]
4250 fn parse_triode_12at7() {
4251 let (_, (c, _)) = component_def("V2: triode(12at7)").unwrap();
4252 assert_eq!(
4253 c.kind.as_any().downcast_ref::<Triode>().unwrap(),
4254 &Triode {
4255 model: "12AT7".into()
4256 }
4257 );
4258 }
4259
4260 #[test]
4261 fn parse_triode_12au7() {
4262 let (_, (c, _)) = component_def("V3: triode(12au7)").unwrap();
4263 assert_eq!(
4264 c.kind.as_any().downcast_ref::<Triode>().unwrap(),
4265 &Triode {
4266 model: "12AU7".into()
4267 }
4268 );
4269 }
4270
4271 #[test]
4272 fn parse_triode_ecc83() {
4273 let (_, (c, _)) = component_def("V4: triode(ecc83)").unwrap();
4275 assert_eq!(
4276 c.kind.as_any().downcast_ref::<Triode>().unwrap(),
4277 &Triode {
4278 model: "ECC83".into()
4279 }
4280 );
4281 }
4282
4283 #[test]
4284 fn parse_triode_12ay7() {
4285 let (_, (c, _)) = component_def("V1: triode(12ay7)").unwrap();
4286 assert_eq!(c.id, "V1");
4287 assert_eq!(
4288 c.kind.as_any().downcast_ref::<Triode>().unwrap(),
4289 &Triode {
4290 model: "12AY7".into()
4291 }
4292 );
4293 }
4294
4295 #[test]
4296 fn parse_triode_6072() {
4297 let (_, (c, _)) = component_def("V1: triode(6072)").unwrap();
4299 assert_eq!(
4300 c.kind.as_any().downcast_ref::<Triode>().unwrap(),
4301 &Triode {
4302 model: "6072".into()
4303 }
4304 );
4305 }
4306
4307 #[test]
4308 fn parse_vari_mu_6386() {
4309 let (_, (c, _)) = component_def("V1: vari_mu(6386)").unwrap();
4310 assert_eq!(c.id, "V1");
4311 assert_eq!(
4312 c.kind.as_any().downcast_ref::<VariMu>().unwrap(),
4313 &VariMu {
4314 model: "6386".into()
4315 }
4316 );
4317 }
4318
4319 #[test]
4320 fn parse_lfo_component() {
4321 let (_, (c, _)) = component_def("LFO1: lfo(triangle, 100k, 220n)").unwrap();
4323 assert_eq!(c.id, "LFO1");
4324 let lfo = c.kind.as_any().downcast_ref::<Lfo>().expect("expected Lfo");
4325 assert_eq!(lfo.waveform, LfoWaveformDsl::Triangle);
4326 assert!((lfo.timing_r - 100_000.0).abs() < 1.0);
4327 assert!((lfo.timing_c - 220e-9).abs() < 1e-12);
4328 }
4329
4330 #[test]
4331 fn parse_lfo_waveforms() {
4332 assert_eq!(lfo_waveform("sine").unwrap().1, LfoWaveformDsl::Sine);
4333 assert_eq!(
4334 lfo_waveform("triangle").unwrap().1,
4335 LfoWaveformDsl::Triangle
4336 );
4337 assert_eq!(lfo_waveform("square").unwrap().1, LfoWaveformDsl::Square);
4338 assert_eq!(lfo_waveform("saw_up").unwrap().1, LfoWaveformDsl::SawUp);
4339 assert_eq!(lfo_waveform("saw_down").unwrap().1, LfoWaveformDsl::SawDown);
4340 assert_eq!(
4341 lfo_waveform("sample_hold").unwrap().1,
4342 LfoWaveformDsl::SampleAndHold
4343 );
4344 }
4345
4346 #[test]
4347 fn parse_pedal_with_lfo_component() {
4348 let src = r#"
4349pedal "Harmonic Tremolo" {
4350 components {
4351 LFO1: lfo(triangle, 100k, 220n)
4352 C1: cap(100n)
4353 J1: njfet(j201)
4354 R1: resistor(10k)
4355 }
4356 nets {
4357 in -> C1.a
4358 C1.b -> J1.drain
4359 J1.source -> gnd
4360 LFO1.out -> J1.vgs
4361 }
4362}
4363"#;
4364 let def = parse_pedal_file(src).unwrap();
4365 assert_eq!(def.name, "Harmonic Tremolo");
4366 assert_eq!(def.components.len(), 4);
4367 let lfo = def.components[0]
4369 .kind
4370 .as_any()
4371 .downcast_ref::<Lfo>()
4372 .expect("expected Lfo component");
4373 assert_eq!(lfo.waveform, LfoWaveformDsl::Triangle);
4374 assert!((lfo.timing_r - 100_000.0).abs() < 1.0);
4375 assert!((lfo.timing_c - 220e-9).abs() < 1e-12);
4376 let lfo_net = def.nets.iter().find(|n| {
4378 matches!(&n.from, Pin::ComponentPin { component, pin }
4379 if component == "LFO1" && pin == "out")
4380 });
4381 assert!(lfo_net.is_some(), "should have LFO1.out net");
4382 }
4383
4384 #[test]
4385 fn parse_envelope_follower_component() {
4386 let (_, (c, _)) = component_def("EF1: envelope_follower(1k, 4.7u, 100k, 1u, 20k)").unwrap();
4388 assert_eq!(c.id, "EF1");
4389 let ef = c
4390 .kind
4391 .as_any()
4392 .downcast_ref::<EnvelopeFollower>()
4393 .expect("expected EnvelopeFollower");
4394 assert!((ef.attack_r - 1_000.0).abs() < 1.0);
4395 assert!((ef.attack_c - 4.7e-6).abs() < 1e-9);
4396 assert!((ef.release_r - 100_000.0).abs() < 1.0);
4397 assert!((ef.release_c - 1e-6).abs() < 1e-9);
4398 assert!((ef.sensitivity_r - 20_000.0).abs() < 1.0);
4399 }
4400
4401 #[test]
4402 fn parse_pedal_with_envelope_follower() {
4403 let src = r#"
4404pedal "Auto Wah" {
4405 components {
4406 EF1: envelope_follower(1k, 4.7u, 100k, 1.5u, 20k)
4407 C1: cap(100n)
4408 J1: njfet(j201)
4409 R1: resistor(10k)
4410 }
4411 nets {
4412 in -> C1.a
4413 C1.b -> J1.drain
4414 J1.source -> gnd
4415 EF1.out -> J1.vgs
4416 R1.a -> J1.drain
4417 R1.b -> out
4418 }
4419}
4420"#;
4421 let def = parse_pedal_file(src).unwrap();
4422 assert_eq!(def.name, "Auto Wah");
4423 assert_eq!(def.components.len(), 4);
4424 let ef = def.components[0]
4426 .kind
4427 .as_any()
4428 .downcast_ref::<EnvelopeFollower>()
4429 .expect("expected EnvelopeFollower component");
4430 assert!((ef.attack_r - 1_000.0).abs() < 1.0);
4431 assert!((ef.attack_c - 4.7e-6).abs() < 1e-9);
4432 assert!((ef.release_r - 100_000.0).abs() < 1.0);
4433 assert!((ef.release_c - 1.5e-6).abs() < 1e-9);
4434 assert!((ef.sensitivity_r - 20_000.0).abs() < 1.0);
4435 let ef_net = def.nets.iter().find(|n| {
4437 matches!(&n.from, Pin::ComponentPin { component, pin }
4438 if component == "EF1" && pin == "out")
4439 });
4440 assert!(ef_net.is_some(), "should have EF1.out net");
4441 }
4442
4443 #[test]
4444 fn parse_opamp_generic() {
4445 let (_, (c, _)) = component_def("U1: opamp()").unwrap();
4446 assert_eq!(c.id, "U1");
4447 assert_eq!(c.kind.op_amp_type(), Some(OpAmpType::Generic));
4448 }
4449
4450 #[test]
4451 fn parse_opamp_tl071() {
4452 let (_, (c, _)) = component_def("U1: opamp(tl071)").unwrap();
4453 assert_eq!(c.kind.op_amp_type(), Some(OpAmpType::Tl071));
4454 assert!((OpAmpType::Tl071.slew_rate() - 13.0).abs() < 0.1);
4456 assert!((OpAmpType::Tl071.gain_bandwidth() - 3e6).abs() < 1e3);
4457 }
4458
4459 #[test]
4460 fn parse_opamp_tl072() {
4461 let (_, (c, _)) = component_def("U1: opamp(tl072)").unwrap();
4462 assert_eq!(c.kind.op_amp_type(), Some(OpAmpType::Tl072));
4463 }
4464
4465 #[test]
4466 fn parse_opamp_jrc4558() {
4467 let (_, (c, _)) = component_def("U1: opamp(jrc4558)").unwrap();
4468 assert_eq!(c.kind.op_amp_type(), Some(OpAmpType::Jrc4558));
4469 }
4470
4471 #[test]
4472 fn parse_opamp_4558_alias() {
4473 let (_, (c, _)) = component_def("U1: opamp(4558)").unwrap();
4475 assert_eq!(c.kind.op_amp_type(), Some(OpAmpType::Jrc4558));
4476 }
4477
4478 #[test]
4479 fn parse_opamp_lm308() {
4480 let (_, (c, _)) = component_def("U1: opamp(lm308)").unwrap();
4481 assert_eq!(c.kind.op_amp_type(), Some(OpAmpType::Lm308));
4482 }
4483
4484 #[test]
4485 fn parse_opamp_ne5532() {
4486 let (_, (c, _)) = component_def("U1: opamp(ne5532)").unwrap();
4487 assert_eq!(c.kind.op_amp_type(), Some(OpAmpType::Ne5532));
4488 }
4489
4490 #[test]
4491 fn parse_opamp_njm5532_alias() {
4492 let (_, (c, _)) = component_def("U1: opamp(njm5532)").unwrap();
4494 assert_eq!(c.kind.op_amp_type(), Some(OpAmpType::Ne5532));
4495 let (_, (c2, _)) = component_def("U2: opamp(5532)").unwrap();
4496 assert_eq!(c2.kind.op_amp_type(), Some(OpAmpType::Ne5532));
4497 }
4498
4499 #[test]
4500 fn parse_opamp_ca3080() {
4501 let (_, (c, _)) = component_def("U1: opamp(ca3080)").unwrap();
4502 assert_eq!(c.kind.op_amp_type(), Some(OpAmpType::Ca3080));
4503 assert!(OpAmpType::Ca3080.is_ota());
4505 }
4506
4507 #[test]
4508 fn opamp_slew_rates() {
4509 assert!((OpAmpType::Lm308.slew_rate() - 0.3).abs() < 0.01);
4511 assert!((OpAmpType::Tl072.slew_rate() - 13.0).abs() < 0.1);
4512 assert!((OpAmpType::Jrc4558.slew_rate() - 1.7).abs() < 0.1);
4513 }
4514
4515 #[test]
4516 fn opamp_gain_bandwidth() {
4517 assert!((OpAmpType::Lm308.gain_bandwidth() - 1e6).abs() < 1e3);
4519 assert!((OpAmpType::Ne5532.gain_bandwidth() - 10e6).abs() < 1e3);
4520 }
4521
4522 #[test]
4525 fn parse_nmos_2n7000() {
4526 let (_, (c, _)) = component_def("M1: nmos(2n7000)").unwrap();
4527 assert_eq!(c.id, "M1");
4528 assert_eq!(
4529 c.kind.as_any().downcast_ref::<Nmos>().unwrap(),
4530 &Nmos {
4531 mosfet_type: MosfetType::N2n7000
4532 }
4533 );
4534 }
4535
4536 #[test]
4537 fn parse_nmos_irf520() {
4538 let (_, (c, _)) = component_def("M1: nmos(irf520)").unwrap();
4539 assert_eq!(
4540 c.kind.as_any().downcast_ref::<Nmos>().unwrap(),
4541 &Nmos {
4542 mosfet_type: MosfetType::Irf520
4543 }
4544 );
4545 }
4546
4547 #[test]
4548 fn parse_pmos_bs250() {
4549 let (_, (c, _)) = component_def("M2: pmos(bs250)").unwrap();
4550 assert_eq!(c.id, "M2");
4551 assert_eq!(
4552 c.kind.as_any().downcast_ref::<Pmos>().unwrap(),
4553 &Pmos {
4554 mosfet_type: MosfetType::Bs250
4555 }
4556 );
4557 }
4558
4559 #[test]
4560 fn parse_pmos_irf9520() {
4561 let (_, (c, _)) = component_def("M2: pmos(irf9520)").unwrap();
4562 assert_eq!(
4563 c.kind.as_any().downcast_ref::<Pmos>().unwrap(),
4564 &Pmos {
4565 mosfet_type: MosfetType::Irf9520
4566 }
4567 );
4568 }
4569
4570 #[test]
4573 fn parse_zener_with_voltage_suffix() {
4574 let (_, (c, _)) = component_def("Z1: zener(5.1v)").unwrap();
4575 assert_eq!(c.id, "Z1");
4576 let z = c
4577 .kind
4578 .as_any()
4579 .downcast_ref::<Zener>()
4580 .expect("expected Zener");
4581 assert!((z.breakdown_voltage - 5.1).abs() < 1e-6);
4582 }
4583
4584 #[test]
4585 fn parse_zener_without_suffix() {
4586 let (_, (c, _)) = component_def("Z2: zener(3.3)").unwrap();
4587 let z = c
4588 .kind
4589 .as_any()
4590 .downcast_ref::<Zener>()
4591 .expect("expected Zener");
4592 assert!((z.breakdown_voltage - 3.3).abs() < 1e-6);
4593 }
4594
4595 #[test]
4596 fn parse_pedal_with_mosfet() {
4597 let src = r#"
4598pedal "MOSFET Drive" {
4599 components {
4600 C1: cap(100n)
4601 R1: resistor(10k)
4602 M1: nmos(2n7000)
4603 R2: resistor(1k)
4604 }
4605 nets {
4606 in -> C1.a
4607 C1.b -> R1.a, M1.drain
4608 R1.b -> gnd
4609 M1.source -> R2.a
4610 R2.b -> gnd
4611 M1.drain -> out
4612 }
4613}
4614"#;
4615 let def = parse_pedal_file(src).unwrap();
4616 assert_eq!(def.name, "MOSFET Drive");
4617 assert_eq!(def.components.len(), 4);
4618 assert!(def.components.iter().any(|c| c
4619 .kind
4620 .as_any()
4621 .downcast_ref::<Nmos>()
4622 .map_or(false, |m| m.mosfet_type == MosfetType::N2n7000)));
4623 }
4624
4625 #[test]
4626 fn parse_pedal_with_zener() {
4627 let src = r#"
4628pedal "Zener Clipper" {
4629 components {
4630 C1: cap(100n)
4631 R1: resistor(10k)
4632 Z1: zener(5.1v)
4633 }
4634 nets {
4635 in -> C1.a
4636 C1.b -> R1.a, Z1.a
4637 R1.b -> gnd
4638 Z1.b -> gnd
4639 }
4640}
4641"#;
4642 let def = parse_pedal_file(src).unwrap();
4643 assert_eq!(def.name, "Zener Clipper");
4644 assert_eq!(def.components.len(), 3);
4645 assert!(def.components.iter().any(|c| c
4646 .kind
4647 .as_any()
4648 .downcast_ref::<Zener>()
4649 .map_or(false, |z| (z.breakdown_voltage - 5.1).abs() < 1e-6)));
4650 }
4651
4652 #[test]
4655 fn parse_bbd_mn3207() {
4656 let (_, (c, _)) = component_def("BBD1: bbd(mn3207)").unwrap();
4657 assert_eq!(c.id, "BBD1");
4658 assert_eq!(
4659 c.kind.as_any().downcast_ref::<Bbd>().unwrap(),
4660 &Bbd {
4661 bbd_type: BbdType::Mn3207
4662 }
4663 );
4664 }
4665
4666 #[test]
4667 fn parse_bbd_mn3007() {
4668 let (_, (c, _)) = component_def("BBD1: bbd(mn3007)").unwrap();
4669 assert_eq!(
4670 c.kind.as_any().downcast_ref::<Bbd>().unwrap(),
4671 &Bbd {
4672 bbd_type: BbdType::Mn3007
4673 }
4674 );
4675 }
4676
4677 #[test]
4678 fn parse_bbd_mn3005() {
4679 let (_, (c, _)) = component_def("BBD1: bbd(mn3005)").unwrap();
4680 assert_eq!(
4681 c.kind.as_any().downcast_ref::<Bbd>().unwrap(),
4682 &Bbd {
4683 bbd_type: BbdType::Mn3005
4684 }
4685 );
4686 }
4687
4688 #[test]
4689 fn parse_pedal_with_bbd() {
4690 let src = r#"
4691pedal "Chorus" {
4692 components {
4693 C1: cap(100n)
4694 R1: resistor(10k)
4695 BBD1: bbd(mn3207)
4696 LFO1: lfo(triangle, 100k, 47n)
4697 }
4698 nets {
4699 in -> C1.a
4700 C1.b -> R1.a, BBD1.in
4701 R1.b -> gnd
4702 BBD1.out -> out
4703 LFO1.out -> BBD1.clock
4704 }
4705}
4706"#;
4707 let def = parse_pedal_file(src).unwrap();
4708 assert_eq!(def.name, "Chorus");
4709 assert_eq!(def.components.len(), 4);
4710 assert!(def.components.iter().any(|c| c
4711 .kind
4712 .as_any()
4713 .downcast_ref::<Bbd>()
4714 .map_or(false, |b| b.bbd_type == BbdType::Mn3207)));
4715 }
4716
4717 #[test]
4720 fn parse_neon_default() {
4721 let (_, (c, _)) = component_def("NE1: neon()").unwrap();
4722 assert_eq!(c.id, "NE1");
4723 assert_eq!(
4724 c.kind.as_any().downcast_ref::<Neon>().unwrap(),
4725 &Neon {
4726 neon_type: NeonType::Ne2
4727 }
4728 );
4729 }
4730
4731 #[test]
4732 fn parse_neon_ne2() {
4733 let (_, (c, _)) = component_def("NE1: neon(ne2)").unwrap();
4734 assert_eq!(
4735 c.kind.as_any().downcast_ref::<Neon>().unwrap(),
4736 &Neon {
4737 neon_type: NeonType::Ne2
4738 }
4739 );
4740 }
4741
4742 #[test]
4743 fn parse_neon_ne2_hyphen() {
4744 let (_, (c, _)) = component_def("NE1: neon(ne-2)").unwrap();
4745 assert_eq!(
4746 c.kind.as_any().downcast_ref::<Neon>().unwrap(),
4747 &Neon {
4748 neon_type: NeonType::Ne2
4749 }
4750 );
4751 }
4752
4753 #[test]
4754 fn parse_neon_ne51() {
4755 let (_, (c, _)) = component_def("NE1: neon(ne51)").unwrap();
4756 assert_eq!(
4757 c.kind.as_any().downcast_ref::<Neon>().unwrap(),
4758 &Neon {
4759 neon_type: NeonType::Ne51
4760 }
4761 );
4762 }
4763
4764 #[test]
4765 fn parse_neon_ne83() {
4766 let (_, (c, _)) = component_def("NE1: neon(ne-83)").unwrap();
4767 assert_eq!(
4768 c.kind.as_any().downcast_ref::<Neon>().unwrap(),
4769 &Neon {
4770 neon_type: NeonType::Ne83
4771 }
4772 );
4773 }
4774
4775 #[test]
4776 fn parse_pedal_with_neon_tremolo() {
4777 let src = r#"
4778pedal "Fender Vibrato" {
4779 components {
4780 R1: resistor(1M)
4781 NE1: neon()
4782 C1: cap(0.1u)
4783 LDR1: photocoupler(vtl5c3)
4784 R2: resistor(10k)
4785 }
4786 nets {
4787 in -> R1.a
4788 R1.b -> LDR1.a
4789 LDR1.b -> out
4790 NE1.a -> C1.a
4791 C1.b -> gnd
4792 NE1.b -> R2.a
4793 R2.b -> gnd
4794 }
4795}
4796"#;
4797 let def = parse_pedal_file(src).unwrap();
4798 assert_eq!(def.name, "Fender Vibrato");
4799 assert!(def.components.iter().any(|c| c
4800 .kind
4801 .as_any()
4802 .downcast_ref::<Neon>()
4803 .map_or(false, |n| n.neon_type == NeonType::Ne2)));
4804 }
4805
4806 #[test]
4809 fn parse_pentode_ef86() {
4810 let (_, (c, _)) = component_def("V1: pentode(ef86)").unwrap();
4811 assert_eq!(c.id, "V1");
4812 assert_eq!(
4813 c.kind.as_any().downcast_ref::<Pentode>().unwrap(),
4814 &Pentode {
4815 model: "EF86".into()
4816 }
4817 );
4818 }
4819
4820 #[test]
4821 fn parse_pentode_el84() {
4822 let (_, (c, _)) = component_def("V1: pentode(el84)").unwrap();
4823 assert_eq!(
4824 c.kind.as_any().downcast_ref::<Pentode>().unwrap(),
4825 &Pentode {
4826 model: "EL84".into()
4827 }
4828 );
4829 }
4830
4831 #[test]
4832 fn parse_pentode_6267() {
4833 let (_, (c, _)) = component_def("V1: pentode(6267)").unwrap();
4835 assert_eq!(
4836 c.kind.as_any().downcast_ref::<Pentode>().unwrap(),
4837 &Pentode {
4838 model: "6267".into()
4839 }
4840 );
4841 }
4842
4843 #[test]
4844 fn parse_pentode_6bq5() {
4845 let (_, (c, _)) = component_def("V1: pentode(6bq5)").unwrap();
4847 assert_eq!(
4848 c.kind.as_any().downcast_ref::<Pentode>().unwrap(),
4849 &Pentode {
4850 model: "6BQ5".into()
4851 }
4852 );
4853 }
4854
4855 #[test]
4856 fn parse_pedal_with_pentode() {
4857 let src = r#"
4858pedal "Vox Preamp" {
4859 components {
4860 C1: cap(20n)
4861 R1: resistor(1M)
4862 V1: pentode(ef86)
4863 R2: resistor(220k)
4864 R3: resistor(2.2k)
4865 C2: cap(25u)
4866 }
4867 nets {
4868 in -> C1.a
4869 C1.b -> R1.a, V1.grid
4870 R1.b -> gnd
4871 vcc -> R2.a
4872 R2.b -> V1.plate
4873 V1.cathode -> R3.a, C2.a
4874 R3.b -> gnd
4875 C2.b -> gnd
4876 V1.plate -> out
4877 }
4878}
4879"#;
4880 let def = parse_pedal_file(src).unwrap();
4881 assert_eq!(def.name, "Vox Preamp");
4882 assert!(def.components.iter().any(|c| c
4883 .kind
4884 .as_any()
4885 .downcast_ref::<Pentode>()
4886 .map_or(false, |p| p.model == "EF86")));
4887 }
4888
4889 #[test]
4890 fn parse_pentode_6l6gc() {
4891 let (_, (c, _)) = component_def("V1: pentode(6l6gc)").unwrap();
4892 assert_eq!(
4893 c.kind.as_any().downcast_ref::<Pentode>().unwrap(),
4894 &Pentode {
4895 model: "6L6GC".into()
4896 }
4897 );
4898 }
4899
4900 #[test]
4901 fn parse_pentode_6l6_alias() {
4902 let (_, (c, _)) = component_def("V1: pentode(6l6)").unwrap();
4904 assert_eq!(
4905 c.kind.as_any().downcast_ref::<Pentode>().unwrap(),
4906 &Pentode {
4907 model: "6L6".into()
4908 }
4909 );
4910 }
4911
4912 #[test]
4913 fn parse_pentode_5881_alias() {
4914 let (_, (c, _)) = component_def("V1: pentode(5881)").unwrap();
4916 assert_eq!(
4917 c.kind.as_any().downcast_ref::<Pentode>().unwrap(),
4918 &Pentode {
4919 model: "5881".into()
4920 }
4921 );
4922 }
4923
4924 #[test]
4925 fn parse_pentode_kt66_alias() {
4926 let (_, (c, _)) = component_def("V1: pentode(kt66)").unwrap();
4928 assert_eq!(
4929 c.kind.as_any().downcast_ref::<Pentode>().unwrap(),
4930 &Pentode {
4931 model: "KT66".into()
4932 }
4933 );
4934 }
4935
4936 #[test]
4937 fn parse_pentode_el34() {
4938 let (_, (c, _)) = component_def("V1: pentode(el34)").unwrap();
4939 assert_eq!(
4940 c.kind.as_any().downcast_ref::<Pentode>().unwrap(),
4941 &Pentode {
4942 model: "EL34".into()
4943 }
4944 );
4945 }
4946
4947 #[test]
4948 fn parse_pentode_6ca7_alias() {
4949 let (_, (c, _)) = component_def("V1: pentode(6ca7)").unwrap();
4951 assert_eq!(
4952 c.kind.as_any().downcast_ref::<Pentode>().unwrap(),
4953 &Pentode {
4954 model: "6CA7".into()
4955 }
4956 );
4957 }
4958
4959 #[test]
4960 fn parse_pentode_kt77_alias() {
4961 let (_, (c, _)) = component_def("V1: pentode(kt77)").unwrap();
4963 assert_eq!(
4964 c.kind.as_any().downcast_ref::<Pentode>().unwrap(),
4965 &Pentode {
4966 model: "KT77".into()
4967 }
4968 );
4969 }
4970
4971 #[test]
4972 fn parse_pentode_6550() {
4973 let (_, (c, _)) = component_def("V1: pentode(6550)").unwrap();
4974 assert_eq!(
4975 c.kind.as_any().downcast_ref::<Pentode>().unwrap(),
4976 &Pentode {
4977 model: "6550".into()
4978 }
4979 );
4980 }
4981
4982 #[test]
4983 fn parse_pentode_kt88_alias() {
4984 let (_, (c, _)) = component_def("V1: pentode(kt88)").unwrap();
4986 assert_eq!(
4987 c.kind.as_any().downcast_ref::<Pentode>().unwrap(),
4988 &Pentode {
4989 model: "KT88".into()
4990 }
4991 );
4992 }
4993
4994 #[test]
4995 fn parse_pentode_kt90_alias() {
4996 let (_, (c, _)) = component_def("V1: pentode(kt90)").unwrap();
4998 assert_eq!(
4999 c.kind.as_any().downcast_ref::<Pentode>().unwrap(),
5000 &Pentode {
5001 model: "KT90".into()
5002 }
5003 );
5004 }
5005
5006 #[test]
5009 fn parse_synth_keyword() {
5010 let src = r#"
5011synth "Test Synth" {
5012 components {
5013 R1: resistor(10k)
5014 }
5015 nets {
5016 in -> R1.a
5017 R1.b -> out
5018 }
5019}
5020"#;
5021 let def = parse_pedal_file(src).unwrap();
5022 assert_eq!(def.name, "Test Synth");
5023 }
5024
5025 #[test]
5026 fn parse_vco_cem3340() {
5027 let (_, (c, _)) = component_def("VCO1: vco(cem3340)").unwrap();
5028 assert_eq!(c.id, "VCO1");
5029 assert_eq!(
5030 c.kind.as_any().downcast_ref::<Vco>().unwrap(),
5031 &Vco {
5032 vco_type: VcoType::Cem3340,
5033 base_freq: 440.0,
5034 waveform: VcoWaveformDsl::Saw
5035 }
5036 );
5037 }
5038
5039 #[test]
5040 fn parse_vco_as3340() {
5041 let (_, (c, _)) = component_def("VCO1: vco(as3340)").unwrap();
5042 assert_eq!(
5043 c.kind.as_any().downcast_ref::<Vco>().unwrap(),
5044 &Vco {
5045 vco_type: VcoType::As3340,
5046 base_freq: 440.0,
5047 waveform: VcoWaveformDsl::Saw
5048 }
5049 );
5050 }
5051
5052 #[test]
5053 fn parse_vco_v3340() {
5054 let (_, (c, _)) = component_def("VCO1: vco(v3340)").unwrap();
5055 assert_eq!(
5056 c.kind.as_any().downcast_ref::<Vco>().unwrap(),
5057 &Vco {
5058 vco_type: VcoType::V3340,
5059 base_freq: 440.0,
5060 waveform: VcoWaveformDsl::Saw
5061 }
5062 );
5063 }
5064
5065 #[test]
5066 fn parse_vco_with_freq() {
5067 let (_, (c, _)) = component_def("VCO1: vco(cem3340, 540)").unwrap();
5068 assert_eq!(
5069 c.kind.as_any().downcast_ref::<Vco>().unwrap(),
5070 &Vco {
5071 vco_type: VcoType::Cem3340,
5072 base_freq: 540.0,
5073 waveform: VcoWaveformDsl::Saw
5074 }
5075 );
5076 }
5077
5078 #[test]
5079 fn parse_vco_with_freq_and_waveform() {
5080 let (_, (c, _)) = component_def("VCO1: vco(cem3340, 540, pulse)").unwrap();
5081 assert_eq!(
5082 c.kind.as_any().downcast_ref::<Vco>().unwrap(),
5083 &Vco {
5084 vco_type: VcoType::Cem3340,
5085 base_freq: 540.0,
5086 waveform: VcoWaveformDsl::Pulse
5087 }
5088 );
5089 }
5090
5091 #[test]
5092 fn parse_vcf_cem3320() {
5093 let (_, (c, _)) = component_def("VCF1: vcf(cem3320)").unwrap();
5094 assert_eq!(c.id, "VCF1");
5095 assert_eq!(
5096 c.kind.as_any().downcast_ref::<Vcf>().unwrap(),
5097 &Vcf {
5098 vcf_type: VcfType::Cem3320
5099 }
5100 );
5101 }
5102
5103 #[test]
5104 fn parse_vcf_as3320() {
5105 let (_, (c, _)) = component_def("VCF1: vcf(as3320)").unwrap();
5106 assert_eq!(
5107 c.kind.as_any().downcast_ref::<Vcf>().unwrap(),
5108 &Vcf {
5109 vcf_type: VcfType::As3320
5110 }
5111 );
5112 }
5113
5114 #[test]
5115 fn parse_vca_ssm2164() {
5116 let (_, (c, _)) = component_def("VCA1: vca(ssm2164)").unwrap();
5117 assert_eq!(c.id, "VCA1");
5118 assert_eq!(
5119 c.kind.as_any().downcast_ref::<Vca>().unwrap(),
5120 &Vca {
5121 vca_type: VcaType::Ssm2164
5122 }
5123 );
5124 }
5125
5126 #[test]
5127 fn parse_vca_v2164() {
5128 let (_, (c, _)) = component_def("VCA1: vca(v2164)").unwrap();
5129 assert_eq!(
5130 c.kind.as_any().downcast_ref::<Vca>().unwrap(),
5131 &Vca {
5132 vca_type: VcaType::V2164
5133 }
5134 );
5135 }
5136
5137 #[test]
5138 fn parse_comparator_lm311() {
5139 let (_, (c, _)) = component_def("U1: comparator(lm311)").unwrap();
5140 assert_eq!(c.id, "U1");
5141 assert_eq!(
5142 c.kind.as_any().downcast_ref::<Comparator>().unwrap(),
5143 &Comparator {
5144 comp_type: ComparatorType::Lm311
5145 }
5146 );
5147 }
5148
5149 #[test]
5150 fn parse_comparator_lm393() {
5151 let (_, (c, _)) = component_def("U1: comparator(lm393)").unwrap();
5152 assert_eq!(
5153 c.kind.as_any().downcast_ref::<Comparator>().unwrap(),
5154 &Comparator {
5155 comp_type: ComparatorType::Lm393
5156 }
5157 );
5158 }
5159
5160 #[test]
5161 fn parse_analog_switch_cd4066() {
5162 let (_, (c, _)) = component_def("SW1: switch(cd4066)").unwrap();
5163 assert_eq!(c.id, "SW1");
5164 assert_eq!(
5165 c.kind.as_any().downcast_ref::<AnalogSwitch>().unwrap(),
5166 &AnalogSwitch {
5167 switch_type: AnalogSwitchType::Cd4066
5168 }
5169 );
5170 }
5171
5172 #[test]
5173 fn parse_analog_switch_dg411() {
5174 let (_, (c, _)) = component_def("SW1: switch(dg411)").unwrap();
5175 assert_eq!(
5176 c.kind.as_any().downcast_ref::<AnalogSwitch>().unwrap(),
5177 &AnalogSwitch {
5178 switch_type: AnalogSwitchType::Dg411
5179 }
5180 );
5181 }
5182
5183 #[test]
5184 fn parse_matched_npn_ssm2210() {
5185 let (_, (c, _)) = component_def("QM1: matched_npn(ssm2210)").unwrap();
5186 assert_eq!(c.id, "QM1");
5187 assert_eq!(
5188 c.kind.as_any().downcast_ref::<MatchedNpn>().unwrap(),
5189 &MatchedNpn {
5190 matched_type: MatchedTransistorType::Ssm2210
5191 }
5192 );
5193 }
5194
5195 #[test]
5196 fn parse_matched_npn_ca3046() {
5197 let (_, (c, _)) = component_def("QM1: matched_npn(ca3046)").unwrap();
5198 assert_eq!(
5199 c.kind.as_any().downcast_ref::<MatchedNpn>().unwrap(),
5200 &MatchedNpn {
5201 matched_type: MatchedTransistorType::Ca3046
5202 }
5203 );
5204 }
5205
5206 #[test]
5207 fn parse_matched_pnp_lm394() {
5208 let (_, (c, _)) = component_def("QM1: matched_pnp(lm394)").unwrap();
5209 assert_eq!(
5210 c.kind.as_any().downcast_ref::<MatchedPnp>().unwrap(),
5211 &MatchedPnp {
5212 matched_type: MatchedTransistorType::Lm394
5213 }
5214 );
5215 }
5216
5217 #[test]
5218 fn parse_tempco() {
5219 let (_, (c, _)) = component_def("RT1: tempco(2k, 3500)").unwrap();
5220 assert_eq!(c.id, "RT1");
5221 let tc = c
5222 .kind
5223 .as_any()
5224 .downcast_ref::<Tempco>()
5225 .expect("expected Tempco");
5226 assert!((tc.resistance - 2000.0).abs() < 1e-6);
5227 assert!((tc.ppm - 3500.0).abs() < 1e-6);
5228 }
5229
5230 #[test]
5231 fn parse_synth_with_cv_gate() {
5232 let src = r#"
5233synth "CV Test" {
5234 components {
5235 VCO1: vco(as3340)
5236 R1: resistor(100k)
5237 VCF1: vcf(as3320)
5238 R2: resistor(100k)
5239 }
5240 nets {
5241 cv_pitch -> R1.a
5242 R1.b -> VCO1.cv
5243 VCO1.saw -> VCF1.in
5244 cv_filter -> R2.a
5245 R2.b -> VCF1.cv
5246 VCF1.out -> out
5247 gate -> gnd
5248 }
5249}
5250"#;
5251 let def = parse_pedal_file(src).unwrap();
5252 assert_eq!(def.name, "CV Test");
5253 assert_eq!(def.components.len(), 4);
5254 let has_reserved = |name: &str| {
5256 def.nets.iter().any(|n| {
5257 matches!(&n.from, Pin::Reserved(s) if s == name)
5258 || n.to
5259 .iter()
5260 .any(|p| matches!(p, Pin::Reserved(s) if s == name))
5261 })
5262 };
5263 assert!(has_reserved("cv_pitch"));
5264 assert!(has_reserved("cv_filter"));
5265 assert!(has_reserved("gate"));
5266 }
5267
5268 #[test]
5271 fn parse_transformer_simple() {
5272 let (_, (c, _)) = component_def("T1: transformer(10:1, 2H)").unwrap();
5273 assert_eq!(c.id, "T1");
5274 let cfg = c.kind.transformer_config().expect("expected Transformer");
5275 assert_eq!(cfg.model, None);
5276 assert!((cfg.turns_ratio - 10.0).abs() < 1e-6);
5277 assert!((cfg.primary_inductance - 2.0).abs() < 1e-6);
5278 assert_eq!(cfg.primary_type, WindingType::Standard);
5279 assert_eq!(cfg.secondary_type, WindingType::Standard);
5280 }
5281
5282 #[test]
5283 fn parse_transformer_with_model_name() {
5284 let (_, (c, _)) = component_def("T1: transformer(10:1, JT11P1)").unwrap();
5285 let cfg = c.kind.transformer_config().expect("expected Transformer");
5286 assert_eq!(cfg.model.as_deref(), Some("JT11P1"));
5287 assert!((cfg.turns_ratio - 10.0).abs() < 1e-6);
5288 assert_eq!(cfg.primary_inductance, 0.0);
5289 assert_eq!(cfg.coupling, 0.0);
5290 }
5291
5292 #[test]
5293 fn parse_transformer_with_winding_types() {
5294 let (_, (c, _)) = component_def("T2: transformer(1.5:1, 4H, pp, ct)").unwrap();
5296 let cfg = c.kind.transformer_config().expect("expected Transformer");
5297 assert!((cfg.turns_ratio - 1.5).abs() < 1e-6);
5298 assert!((cfg.primary_inductance - 4.0).abs() < 1e-6);
5299 assert_eq!(cfg.primary_type, WindingType::PushPull);
5300 assert_eq!(cfg.secondary_type, WindingType::CenterTap);
5301 }
5302
5303 #[test]
5304 fn parse_transformer_with_dcr_and_coupling() {
5305 let (_, (c, _)) = component_def("T3: transformer(5:1, 1H, dcr=50, k=0.98)").unwrap();
5307 let cfg = c.kind.transformer_config().expect("expected Transformer");
5308 assert!((cfg.turns_ratio - 5.0).abs() < 1e-6);
5309 assert!((cfg.primary_dcr - 50.0).abs() < 1e-6);
5310 assert!((cfg.secondary_dcr - 50.0).abs() < 1e-6); assert!((cfg.coupling - 0.98).abs() < 1e-6);
5312 }
5313
5314 #[test]
5315 fn parse_transformer_with_linear_model_fields() {
5316 let (_, (c, _)) = component_def(
5317 "T4: transformer(10:1, JT11P1, Lp=2H, Llp=20m, Lls=200u, Lm=1.98H, Rc=100k, Idc=45m)",
5318 )
5319 .unwrap();
5320 let cfg = c.kind.transformer_config().expect("expected Transformer");
5321 assert_eq!(cfg.model.as_deref(), Some("JT11P1"));
5322 assert!((cfg.primary_inductance - 2.0).abs() < 1e-9);
5323 assert!((cfg.primary_leakage.unwrap() - 20e-3).abs() < 1e-9);
5324 assert!((cfg.secondary_leakage.unwrap() - 200e-6).abs() < 1e-12);
5325 assert!((cfg.magnetizing_inductance.unwrap() - 1.98).abs() < 1e-9);
5326 assert!((cfg.core_loss_resistance.unwrap() - 100_000.0).abs() < 1e-6);
5327 assert!((cfg.dc_bias_current.unwrap() - 45e-3).abs() < 1e-12);
5328 }
5329
5330 #[test]
5331 fn parse_transformer_with_tertiary() {
5332 let (_, (c, _)) =
5333 component_def("T_sc: transformer(4:1, 5.7H, 10, 10p, tertiary=9.5:1)").unwrap();
5334 let cfg = c.kind.transformer_config().expect("expected Transformer");
5335 assert!((cfg.turns_ratio - 4.0).abs() < 1e-6);
5336 assert!((cfg.primary_inductance - 5.7).abs() < 1e-6);
5337 assert!((cfg.primary_dcr - 10.0).abs() < 1e-6);
5338 assert!((cfg.capacitance - 10e-12).abs() < 1e-15);
5339 assert!(cfg.tertiary_turns_ratio.is_some());
5340 assert!((cfg.tertiary_turns_ratio.unwrap() - 9.5).abs() < 1e-6);
5341 }
5342
5343 #[test]
5344 fn parse_transformer_no_tertiary() {
5345 let (_, (c, _)) = component_def("T1: transformer(10:1, 2H)").unwrap();
5346 let cfg = c.kind.transformer_config().expect("expected Transformer");
5347 assert!(cfg.tertiary_turns_ratio.is_none());
5348 }
5349
5350 #[test]
5351 fn parse_cap_switched() {
5352 let (_, (c, _)) = component_def("C_lf: cap_switched(27n, 68n, 220n, 1.5u)").unwrap();
5353 assert_eq!(c.id, "C_lf");
5354 let cs = c
5355 .kind
5356 .as_any()
5357 .downcast_ref::<CapSwitched>()
5358 .expect("expected CapSwitched");
5359 assert_eq!(cs.values.len(), 4);
5360 assert!((cs.values[0] - 27e-9).abs() < 1e-12);
5361 assert!((cs.values[1] - 68e-9).abs() < 1e-12);
5362 assert!((cs.values[2] - 220e-9).abs() < 1e-12);
5363 assert!((cs.values[3] - 1.5e-6).abs() < 1e-12);
5364 }
5365
5366 #[test]
5367 fn parse_inductor_switched() {
5368 let (_, (c, _)) =
5369 component_def("L_hf: inductor_switched(27m, 33m, 47m, 68m, 82m, 150m)").unwrap();
5370 assert_eq!(c.id, "L_hf");
5371 let is = c
5372 .kind
5373 .as_any()
5374 .downcast_ref::<InductorSwitched>()
5375 .expect("expected InductorSwitched");
5376 assert_eq!(is.values.len(), 6);
5377 assert!((is.values[0] - 27e-3).abs() < 1e-6);
5378 assert!((is.values[5] - 150e-3).abs() < 1e-6);
5379 }
5380
5381 #[test]
5382 fn parse_rotary_switch() {
5383 let (_, (c, _)) = component_def(r#"SW1: rotary("20Hz", "30Hz", "60Hz", "100Hz")"#).unwrap();
5384 assert_eq!(c.id, "SW1");
5385 let rs = c
5386 .kind
5387 .as_any()
5388 .downcast_ref::<RotarySwitch>()
5389 .expect("expected RotarySwitch");
5390 assert_eq!(rs.linked_ids.len(), 4);
5391 assert_eq!(rs.linked_ids[0], "20Hz");
5392 assert_eq!(rs.linked_ids[3], "100Hz");
5393 }
5394
5395 #[test]
5396 fn parse_studio_equipment_file() {
5397 let src = r#"
5398equipment "Test EQ" {
5399 components {
5400 T1: transformer(10:1, 2H, pp, ct)
5401 C_lf: cap_switched(27n, 68n, 220n)
5402 L_hf: inductor_switched(27m, 47m, 82m)
5403 SW_freq: rotary("100Hz", "200Hz", "400Hz")
5404 R1: resistor(10k)
5405 }
5406 nets {
5407 in -> T1.a
5408 T1.b -> C_lf.a
5409 C_lf.b -> L_hf.a
5410 L_hf.b -> R1.a
5411 R1.b -> out
5412 }
5413}
5414"#;
5415 let def = parse_pedal_file(src).unwrap();
5416 assert_eq!(def.name, "Test EQ");
5417 assert_eq!(def.components.len(), 5);
5418 assert!(def.components.iter().any(|c| c.kind.is_transformer()));
5419 assert!(def.components.iter().any(|c| c
5420 .kind
5421 .as_any()
5422 .downcast_ref::<CapSwitched>()
5423 .is_some()));
5424 assert!(def.components.iter().any(|c| c
5425 .kind
5426 .as_any()
5427 .downcast_ref::<InductorSwitched>()
5428 .is_some()));
5429 assert!(def.components.iter().any(|c| c
5430 .kind
5431 .as_any()
5432 .downcast_ref::<RotarySwitch>()
5433 .is_some()));
5434 }
5435
5436 #[test]
5439 fn parse_supply_9v() {
5440 let src = r#"
5441pedal "9V Pedal" {
5442 supply 9V
5443 components {
5444 R1: resistor(10k)
5445 }
5446 nets {
5447 in -> R1.a
5448 R1.b -> out
5449 }
5450}
5451"#;
5452 let def = parse_pedal_file(src).unwrap();
5453 assert_eq!(def.name, "9V Pedal");
5454 assert_eq!(def.supplies.len(), 1);
5455 let supply = &def.supplies[0];
5456 assert_eq!(supply.name, "vcc");
5457 assert_eq!(supply.config.voltage, 9.0);
5458 assert!(!supply.config.has_sag());
5459 }
5460
5461 #[test]
5462 fn parse_supply_250v_tube_amp() {
5463 let src = r#"
5464pedal "Tube Amp" {
5465 supply 250V
5466 components {
5467 V1: triode(12ax7)
5468 R1: resistor(100k)
5469 }
5470 nets {
5471 in -> V1.grid
5472 V1.plate -> R1.a
5473 R1.b -> out
5474 }
5475}
5476"#;
5477 let def = parse_pedal_file(src).unwrap();
5478 assert_eq!(def.name, "Tube Amp");
5479 assert_eq!(def.supplies[0].config.voltage, 250.0);
5480 }
5481
5482 #[test]
5483 fn parse_supply_lowercase_v() {
5484 let src = r#"
5485pedal "Test" {
5486 supply 12v
5487 components {
5488 R1: resistor(10k)
5489 }
5490 nets {
5491 in -> out
5492 }
5493}
5494"#;
5495 let def = parse_pedal_file(src).unwrap();
5496 assert_eq!(def.supplies[0].config.voltage, 12.0);
5497 }
5498
5499 #[test]
5500 fn parse_supply_decimal() {
5501 let src = r#"
5502pedal "Test" {
5503 supply 9.6V
5504 components {
5505 R1: resistor(10k)
5506 }
5507 nets {
5508 in -> out
5509 }
5510}
5511"#;
5512 let def = parse_pedal_file(src).unwrap();
5513 assert!((def.supplies[0].config.voltage - 9.6).abs() < 0.01);
5514 }
5515
5516 #[test]
5517 fn parse_no_supply_defaults_empty() {
5518 let src = r#"
5519pedal "Default" {
5520 components {
5521 R1: resistor(10k)
5522 }
5523 nets {
5524 in -> out
5525 }
5526}
5527"#;
5528 let def = parse_pedal_file(src).unwrap();
5529 assert!(def.supplies.is_empty());
5530 }
5531
5532 #[test]
5533 fn parse_supply_block_tube_rectifier() {
5534 let src = r#"
5535pedal "Tube Amp Sag" {
5536 supply 480V {
5537 impedance: 150
5538 filter_cap: 40u
5539 rectifier: tube
5540 }
5541 components {
5542 V1: triode(12ax7)
5543 R1: resistor(100k)
5544 }
5545 nets {
5546 in -> V1.grid
5547 V1.plate -> R1.a
5548 R1.b -> out
5549 }
5550}
5551"#;
5552 let def = parse_pedal_file(src).unwrap();
5553 let supply = &def.supplies[0].config;
5554 assert_eq!(supply.voltage, 480.0);
5555 assert!((supply.impedance.unwrap() - 150.0).abs() < 1e-6);
5556 assert!((supply.filter_cap.unwrap() - 40e-6).abs() < 1e-12);
5557 assert_eq!(supply.rectifier, RectifierType::Tube);
5558 assert!(supply.has_sag());
5559 }
5560
5561 #[test]
5562 fn parse_supply_block_solid_state() {
5563 let src = r#"
5564pedal "Modern Amp" {
5565 supply 480V {
5566 impedance: 5
5567 filter_cap: 220u
5568 rectifier: solid_state
5569 }
5570 components {
5571 R1: resistor(10k)
5572 }
5573 nets {
5574 in -> R1.a
5575 R1.b -> out
5576 }
5577}
5578"#;
5579 let def = parse_pedal_file(src).unwrap();
5580 let supply = &def.supplies[0].config;
5581 assert_eq!(supply.voltage, 480.0);
5582 assert!((supply.impedance.unwrap() - 5.0).abs() < 1e-6);
5583 assert!((supply.filter_cap.unwrap() - 220e-6).abs() < 1e-12);
5584 assert_eq!(supply.rectifier, RectifierType::SolidState);
5585 }
5586
5587 #[test]
5588 fn parse_supply_block_with_comments() {
5589 let src = r#"
5590pedal "Commented Supply" {
5591 supply 400V {
5592 impedance: 100 # tube rectifier + transformer resistance
5593 filter_cap: 47u # main filter cap
5594 rectifier: tube # GZ34
5595 }
5596 components {
5597 R1: resistor(10k)
5598 }
5599 nets {
5600 in -> R1.a
5601 R1.b -> out
5602 }
5603}
5604"#;
5605 let def = parse_pedal_file(src).unwrap();
5606 let supply = &def.supplies[0].config;
5607 assert_eq!(supply.voltage, 400.0);
5608 assert!((supply.impedance.unwrap() - 100.0).abs() < 1e-6);
5609 assert!((supply.filter_cap.unwrap() - 47e-6).abs() < 1e-12);
5610 assert_eq!(supply.rectifier, RectifierType::Tube);
5611 }
5612
5613 #[test]
5616 fn parse_supplies_block_dual_rail() {
5617 let src = r#"
5618pedal "Dual Supply" {
5619 supplies {
5620 V+: 15V
5621 V-: -15V
5622 }
5623 components {
5624 R1: resistor(10k)
5625 }
5626 nets {
5627 in -> R1.a
5628 R1.b -> out
5629 }
5630}
5631"#;
5632 let def = parse_pedal_file(src).unwrap();
5633 assert_eq!(def.supplies.len(), 2);
5634
5635 let vpos = &def.supplies[0];
5636 assert_eq!(vpos.name, "V+");
5637 assert_eq!(vpos.config.voltage, 15.0);
5638
5639 let vneg = &def.supplies[1];
5640 assert_eq!(vneg.name, "V-");
5641 assert_eq!(vneg.config.voltage, -15.0);
5642 }
5643
5644 #[test]
5645 fn parse_supplies_block_tube_multiple_rails() {
5646 let src = r#"
5647pedal "Tube Amp Multi-Rail" {
5648 supplies {
5649 B+: 300V { impedance: 100, rectifier: tube }
5650 bias: -50V
5651 filament: 6.3V
5652 }
5653 components {
5654 V1: triode(12ax7)
5655 R1: resistor(100k)
5656 }
5657 nets {
5658 in -> V1.grid
5659 V1.plate -> R1.a
5660 R1.b -> out
5661 }
5662}
5663"#;
5664 let def = parse_pedal_file(src).unwrap();
5665 assert_eq!(def.supplies.len(), 3);
5666
5667 let bplus = &def.supplies[0];
5668 assert_eq!(bplus.name, "B+");
5669 assert_eq!(bplus.config.voltage, 300.0);
5670 assert!(bplus.config.has_sag());
5671 assert_eq!(bplus.config.rectifier, RectifierType::Tube);
5672
5673 let bias = &def.supplies[1];
5674 assert_eq!(bias.name, "bias");
5675 assert_eq!(bias.config.voltage, -50.0);
5676 assert!(!bias.config.has_sag());
5677
5678 let filament = &def.supplies[2];
5679 assert_eq!(filament.name, "filament");
5680 assert_eq!(filament.config.voltage, 6.3);
5681 }
5682
5683 #[test]
5684 fn parse_supplies_vcc_legacy_compat() {
5685 let src = r#"
5687pedal "Legacy" {
5688 supply 9V
5689 components {
5690 R1: resistor(10k)
5691 }
5692 nets {
5693 in -> R1.a
5694 R1.b -> out
5695 }
5696}
5697"#;
5698 let def = parse_pedal_file(src).unwrap();
5699 assert_eq!(def.supplies.len(), 1);
5700 assert_eq!(def.supplies[0].name, "vcc");
5701 assert_eq!(def.supplies[0].config.voltage, 9.0);
5702 }
5703
5704 #[test]
5705 fn pedal_def_helper_methods() {
5706 let src = r#"
5707pedal "Test Helpers" {
5708 supplies {
5709 V+: 15V
5710 V-: -15V
5711 vcc: 5V
5712 }
5713 components {
5714 R1: resistor(10k)
5715 }
5716 nets {
5717 in -> out
5718 }
5719}
5720"#;
5721 let def = parse_pedal_file(src).unwrap();
5722
5723 assert_eq!(def.primary_supply_voltage(), 15.0);
5725
5726 assert!(def.get_supply("V+").is_some());
5728 assert!(def.get_supply("V-").is_some());
5729 assert!(def.get_supply("vcc").is_some());
5730 assert!(def.get_supply("nonexistent").is_none());
5731
5732 let names = def.supply_names();
5734 assert_eq!(names.len(), 3);
5735 assert!(names.contains(&"V+"));
5736
5737 assert!(def.is_supply_rail("V+"));
5739 assert!(def.is_supply_rail("V-"));
5740 assert!(!def.is_supply_rail("gnd"));
5741 }
5742
5743 #[test]
5744 fn parse_supply_rails_in_nets() {
5745 let src = r#"
5747pedal "Bipolar Op-Amp" {
5748 supplies {
5749 V+: 15V
5750 V-: -15V
5751 }
5752 components {
5753 R1: resistor(10k)
5754 R2: resistor(10k)
5755 C1: cap(100u)
5756 }
5757 nets {
5758 V+ -> R1.a
5759 R1.b -> R2.a, C1.a
5760 R2.b -> gnd
5761 C1.b -> gnd
5762 V- -> R2.b
5763 in -> out
5764 }
5765}
5766"#;
5767 let def = parse_pedal_file(src).unwrap();
5768 assert_eq!(def.nets.len(), 6);
5769
5770 let v_plus_net = &def.nets[0];
5772 assert_eq!(v_plus_net.from, Pin::Reserved("V+".to_string()));
5773
5774 let v_minus_net = &def.nets[4];
5776 assert_eq!(v_minus_net.from, Pin::Reserved("V-".to_string()));
5777 }
5778
5779 #[test]
5782 fn parse_monitors_section() {
5783 let src = r#"
5784pedal "Tube Preamp" {
5785 supply 250V
5786 components {
5787 V1: triode(12ax7)
5788 R1: resistor(100k)
5789 }
5790 nets {
5791 in -> V1.grid
5792 V1.plate -> R1.a
5793 R1.b -> out
5794 }
5795 monitors {
5796 V1.plate_current -> "Tube 1" [vu]
5797 output -> "Output Level" [ppm]
5798 input -> "Input Level" [peak]
5799 }
5800}
5801"#;
5802 let def = parse_pedal_file(src).unwrap();
5803 assert_eq!(def.monitors.len(), 3);
5804
5805 let tube_mon = &def.monitors[0];
5806 assert_eq!(tube_mon.component, "V1");
5807 assert_eq!(tube_mon.property, "plate_current");
5808 assert_eq!(tube_mon.label, "Tube 1");
5809 assert_eq!(tube_mon.meter_type, MeterType::Vu);
5810
5811 let output_mon = &def.monitors[1];
5812 assert_eq!(output_mon.component, "output");
5813 assert_eq!(output_mon.property, "level");
5814 assert_eq!(output_mon.meter_type, MeterType::Ppm);
5815
5816 let input_mon = &def.monitors[2];
5817 assert_eq!(input_mon.component, "input");
5818 assert_eq!(input_mon.meter_type, MeterType::Peak);
5819 }
5820
5821 #[test]
5822 fn parse_monitors_gain_reduction() {
5823 let src = r#"
5824pedal "Compressor" {
5825 components {
5826 R1: resistor(10k)
5827 }
5828 nets {
5829 in -> R1.a
5830 R1.b -> out
5831 }
5832 monitors {
5833 GR.reduction -> "Gain Reduction" [gr]
5834 supply.sag -> "Sag" [sag]
5835 }
5836}
5837"#;
5838 let def = parse_pedal_file(src).unwrap();
5839 assert_eq!(def.monitors.len(), 2);
5840 assert_eq!(def.monitors[0].meter_type, MeterType::GainReduction);
5841 assert_eq!(def.monitors[1].meter_type, MeterType::SupplySag);
5842 }
5843
5844 #[test]
5845 fn parse_empty_monitors() {
5846 let src = r#"
5847pedal "No Monitors" {
5848 components {
5849 R1: resistor(10k)
5850 }
5851 nets {
5852 in -> out
5853 }
5854}
5855"#;
5856 let def = parse_pedal_file(src).unwrap();
5857 assert!(def.monitors.is_empty());
5858 }
5859
5860 #[test]
5865 fn eng_value_embedded_decimal_resistance() {
5866 let cases: &[(&str, f64)] = &[
5867 ("4k7", 4700.0),
5868 ("2k2", 2200.0),
5869 ("1M5", 1_500_000.0),
5870 ("4R7", 4.7),
5871 ("0R47", 0.47),
5872 ("100R", 100.0),
5873 ("47R", 47.0),
5874 ("10R", 10.0),
5875 ];
5876 for &(input, expected) in cases {
5877 let (rest, v) =
5878 eng_value(input).unwrap_or_else(|e| panic!("failed to parse {:?}: {}", input, e));
5879 assert!(rest.is_empty(), "trailing input {:?} for {:?}", rest, input);
5880 assert!(
5881 (v - expected).abs() < expected.abs() * 1e-9 + 1e-15,
5882 "{:?}: got {} expected {}",
5883 input,
5884 v,
5885 expected
5886 );
5887 }
5888 }
5889
5890 #[test]
5891 fn eng_value_embedded_decimal_capacitance() {
5892 let cases: &[(&str, f64)] = &[
5893 ("2u5", 2.5e-6),
5894 ("4u7", 4.7e-6),
5895 ("4n7", 4.7e-9),
5896 ("2n2", 2.2e-9),
5897 ("1p5", 1.5e-12),
5898 ];
5899 for &(input, expected) in cases {
5900 let (rest, v) =
5901 eng_value(input).unwrap_or_else(|e| panic!("failed to parse {:?}: {}", input, e));
5902 assert!(rest.is_empty(), "trailing input {:?} for {:?}", rest, input);
5903 assert!(
5904 (v - expected).abs() < expected.abs() * 1e-9,
5905 "{:?}: got {:.6e} expected {:.6e}",
5906 input,
5907 v,
5908 expected
5909 );
5910 }
5911 }
5912
5913 #[test]
5914 fn eng_value_embedded_decimal_inductance() {
5915 let cases: &[(&str, f64)] = &[("4m7", 4.7e-3), ("2m2", 2.2e-3)];
5916 for &(input, expected) in cases {
5917 let (rest, v) =
5918 eng_value(input).unwrap_or_else(|e| panic!("failed to parse {:?}: {}", input, e));
5919 assert!(rest.is_empty(), "trailing input {:?} for {:?}", rest, input);
5920 assert!(
5921 (v - expected).abs() < expected.abs() * 1e-9,
5922 "{:?}: got {:.6e} expected {:.6e}",
5923 input,
5924 v,
5925 expected
5926 );
5927 }
5928 }
5929
5930 #[test]
5931 fn eng_value_existing_formats_unchanged() {
5932 let cases: &[(&str, f64)] = &[
5933 ("10k", 10_000.0),
5934 ("100n", 100e-9),
5935 ("470p", 470e-12),
5936 ("1u", 1e-6),
5937 ("100m", 100e-3),
5938 ("1M", 1_000_000.0),
5939 ("47", 47.0),
5940 ("0.1", 0.1),
5941 ("3.3k", 3300.0),
5942 ("4.7k", 4700.0),
5943 ("220n", 220e-9),
5944 ];
5945 for &(input, expected) in cases {
5946 let (rest, v) =
5947 eng_value(input).unwrap_or_else(|e| panic!("failed to parse {:?}: {}", input, e));
5948 assert!(rest.is_empty(), "trailing input {:?} for {:?}", rest, input);
5949 assert!(
5950 (v - expected).abs() < expected.abs() * 1e-9 + 1e-15,
5951 "{:?}: got {} expected {}",
5952 input,
5953 v,
5954 expected
5955 );
5956 }
5957 }
5958
5959 #[test]
5960 fn eng_value_uppercase_k() {
5961 let (rest, v) = eng_value("4K7").unwrap();
5962 assert!(rest.is_empty());
5963 assert!((v - 4700.0).abs() < 1e-6);
5964 }
5965
5966 #[test]
5967 fn component_defs_with_shorthand() {
5968 let (_, (c, _)) = component_def("R1: resistor(4k7)").unwrap();
5970 assert_eq!(
5971 c.kind.as_any().downcast_ref::<Resistor>().unwrap(),
5972 &Resistor { value: 4700.0 }
5973 );
5974
5975 let (_, (c, _)) = component_def("C1: cap(4n7)").unwrap();
5977 let cap = c
5978 .kind
5979 .as_any()
5980 .downcast_ref::<Capacitor>()
5981 .expect("expected Capacitor");
5982 assert!((cap.config.value - 4.7e-9).abs() < 1e-18);
5983
5984 let (_, (c, _)) = component_def("L1: inductor(4m7)").unwrap();
5986 assert_eq!(
5987 c.kind.as_any().downcast_ref::<Inductor>().unwrap(),
5988 &Inductor { value: 4.7e-3 }
5989 );
5990 }
5991
5992 #[test]
5993 fn full_pedal_with_shorthand_values() {
5994 let src = r#"
5995pedal "Shorthand Test" {
5996 components {
5997 R1: resistor(4k7)
5998 R2: resistor(2k2)
5999 R3: resistor(4R7)
6000 C1: cap(2u5)
6001 C2: cap(4n7)
6002 L1: inductor(4m7)
6003 }
6004 nets {
6005 in -> R1.a
6006 R1.b -> R2.a
6007 R2.b -> R3.a
6008 R3.b -> C1.a
6009 C1.b -> C2.a
6010 C2.b -> L1.a
6011 L1.b -> out
6012 }
6013}
6014"#;
6015 let def = parse_pedal_file(src).unwrap();
6016 assert_eq!(def.components.len(), 6);
6017 }
6018
6019 #[test]
6020 fn parse_subcircuit_basic() {
6021 let src = r#"
6022equipment "Test Compressor" {
6023 supply 30V
6024
6025 subcircuit sidechain {
6026 rate: 1/64
6027
6028 components {
6029 R1: resistor(10k)
6030 C1: cap(1u)
6031 }
6032 nets {
6033 in -> R1.a
6034 R1.b -> C1.a, out
6035 C1.b -> gnd
6036 }
6037 }
6038
6039 subcircuit audio {
6040 components {
6041 R2: resistor(1k)
6042 }
6043 nets {
6044 in -> R2.a
6045 R2.b -> out
6046 }
6047 }
6048
6049 nets {
6050 in -> audio.in, sidechain.in
6051 sidechain.out -> audio.ctrl
6052 audio.out -> out
6053 }
6054}
6055"#;
6056 let def = parse_pedal_file(src).unwrap();
6057 assert_eq!(def.subcircuits.len(), 2);
6058 assert_eq!(def.subcircuits[0].name, "sidechain");
6059 assert_eq!(def.subcircuits[0].rate, Some(64));
6060 assert_eq!(def.subcircuits[1].name, "audio");
6061 assert_eq!(def.subcircuits[1].rate, None);
6062
6063 let has_sc_port = def.nets.iter().any(|n| {
6065 n.to.iter().any(
6066 |p| matches!(p, Pin::SubcircuitPort { subcircuit, .. } if subcircuit == "audio"),
6067 )
6068 });
6069 assert!(
6070 has_sc_port,
6071 "top-level nets should contain SubcircuitPort after resolution"
6072 );
6073 }
6074
6075 #[test]
6076 fn parse_subcircuit_with_controls() {
6077 let src = r#"
6078equipment "Test" {
6079 supply 9V
6080
6081 subcircuit main {
6082 components {
6083 Vol: pot(100k, a)
6084 R1: resistor(1k)
6085 }
6086 nets {
6087 in -> R1.a
6088 R1.b -> Vol.a
6089 Vol.w -> out
6090 Vol.b -> gnd
6091 }
6092 controls {
6093 Vol.position -> "Volume" [0.0, 1.0] = 0.5
6094 }
6095 }
6096
6097 nets {
6098 in -> main.in
6099 main.out -> out
6100 }
6101}
6102"#;
6103 let def = parse_pedal_file(src).unwrap();
6104 assert_eq!(def.subcircuits.len(), 1);
6105 assert_eq!(def.subcircuits[0].controls.len(), 1);
6106 assert_eq!(def.subcircuits[0].controls[0].label, "Volume");
6107 }
6108
6109 #[test]
6110 fn parse_rate_power_of_two() {
6111 for rate_str in &[
6113 "1/2", "1/4", "1/8", "1/16", "1/32", "1/64", "1/128", "1/256",
6114 ] {
6115 let src = format!("rate: {}", rate_str);
6116 assert!(
6117 parse_rate(&src).is_ok(),
6118 "rate {} should be valid",
6119 rate_str
6120 );
6121 }
6122 }
6123
6124 #[test]
6125 fn parse_no_subcircuits_backward_compat() {
6126 let src = r#"
6128pedal "Simple" {
6129 supply 9V
6130 components {
6131 R1: resistor(1k)
6132 }
6133 nets {
6134 in -> R1.a
6135 R1.b -> out
6136 }
6137}
6138"#;
6139 let def = parse_pedal_file(src).unwrap();
6140 assert!(def.subcircuits.is_empty());
6141 assert_eq!(def.components.len(), 1);
6142 }
6143
6144 #[test]
6147 fn parse_ports_section() {
6148 let src = r#"pedal "test" {
6149 supply 9V
6150 ports {
6151 audio_in: input
6152 cv_cutoff: input
6153 audio_out: output
6154 env_out: output
6155 }
6156 components { R1: resistor(10k) }
6157 nets { audio_in -> R1.a R1.b -> audio_out }
6158 controls {}
6159}"#;
6160 let def = parse_pedal_file(src).unwrap();
6161 assert_eq!(def.ports.len(), 4, "should have 4 ports");
6162 assert_eq!(def.ports[0].name, "audio_in");
6163 assert_eq!(def.ports[0].direction, pedalkernel_rt::PortDirection::Input);
6164 assert_eq!(def.ports[1].name, "cv_cutoff");
6165 assert_eq!(def.ports[1].direction, pedalkernel_rt::PortDirection::Input);
6166 assert_eq!(def.ports[2].name, "audio_out");
6167 assert_eq!(
6168 def.ports[2].direction,
6169 pedalkernel_rt::PortDirection::Output
6170 );
6171 assert_eq!(def.ports[3].name, "env_out");
6172 assert_eq!(
6173 def.ports[3].direction,
6174 pedalkernel_rt::PortDirection::Output
6175 );
6176 }
6177
6178 #[test]
6179 fn parse_no_ports_section() {
6180 let src = r#"pedal "test" {
6181 supply 9V
6182 components { R1: resistor(10k) }
6183 nets { in -> R1.a R1.b -> out }
6184 controls {}
6185}"#;
6186 let def = parse_pedal_file(src).unwrap();
6187 assert!(
6188 def.ports.is_empty(),
6189 "should have no ports when section omitted"
6190 );
6191 }
6192
6193 #[test]
6194 fn ports_used_in_nets() {
6195 let src = r#"pedal "test" {
6197 supply 9V
6198 ports {
6199 audio_in: input
6200 cv_cutoff: input
6201 audio_out: output
6202 }
6203 components {
6204 R1: resistor(10k)
6205 R_cv: resistor(100k)
6206 }
6207 nets {
6208 audio_in -> R1.a
6209 cv_cutoff -> R_cv.a
6210 R_cv.b -> R1.a
6211 R1.b -> audio_out
6212 }
6213 controls {}
6214}"#;
6215 let def = parse_pedal_file(src).unwrap();
6216 assert_eq!(def.ports.len(), 3);
6217 let has_audio_in = def.nets.iter().any(|n| {
6219 matches!(&n.from, Pin::Reserved(s) if s == "audio_in")
6220 || n.to
6221 .iter()
6222 .any(|p| matches!(p, Pin::Reserved(s) if s == "audio_in"))
6223 });
6224 assert!(has_audio_in, "audio_in should be in nets as reserved pin");
6225 }
6226
6227 #[test]
6230 fn parse_port_with_impedance() {
6231 let src = r#"pedal "test" {
6232 supply 9V
6233 ports {
6234 audio_in: input(10k)
6235 cv_cutoff: input(47k)
6236 audio_out: output(600)
6237 }
6238 components { R1: resistor(10k) }
6239 nets { audio_in -> R1.a R1.b -> audio_out }
6240 controls {}
6241}"#;
6242 let def = parse_pedal_file(src).unwrap();
6243 assert_eq!(def.ports.len(), 3);
6244 assert_eq!(def.ports[0].name, "audio_in");
6245 assert_eq!(def.ports[0].impedance, Some(10_000.0));
6246 assert_eq!(def.ports[1].name, "cv_cutoff");
6247 assert_eq!(def.ports[1].impedance, Some(47_000.0));
6248 assert_eq!(def.ports[2].name, "audio_out");
6249 assert_eq!(def.ports[2].impedance, Some(600.0));
6250 }
6251
6252 #[test]
6253 fn parse_port_without_impedance_backward_compat() {
6254 let src = r#"pedal "test" {
6255 supply 9V
6256 ports {
6257 audio_in: input
6258 audio_out: output
6259 }
6260 components { R1: resistor(10k) }
6261 nets { audio_in -> R1.a R1.b -> audio_out }
6262 controls {}
6263}"#;
6264 let def = parse_pedal_file(src).unwrap();
6265 assert_eq!(def.ports.len(), 2);
6266 assert_eq!(def.ports[0].impedance, None);
6267 assert_eq!(def.ports[1].impedance, None);
6268 }
6269
6270 #[test]
6271 fn parse_op_block_with_node_voltage_seeds() {
6272 let src = r#"pedal "test" {
6275 supply 24V
6276 components { Q1: npn(BC184C) R1: resistor(10k) C1: cap(10u) }
6277 nets { in -> C1.a C1.b -> Q1.base, R1.a R1.b -> gnd Q1.collector -> out Q1.emitter -> gnd }
6278 controls {}
6279 op {
6280 Q1: { vbe: 0.64, vce: 5.35 }
6281 nodes {
6282 Q1.base: 1.0308
6283 Q1.collector: 5.7426
6284 C1.a: 0.0
6285 }
6286 }
6287}"#;
6288 let def = parse_pedal_file(src).unwrap();
6289 let explicit: Vec<_> = def
6290 .init_hints
6291 .iter()
6292 .filter(|h| matches!(h.state, InitState::Explicit { .. }))
6293 .collect();
6294 let nodes: Vec<_> = def
6295 .init_hints
6296 .iter()
6297 .filter(|h| matches!(h.state, InitState::NodeVoltage { .. }))
6298 .collect();
6299 assert_eq!(explicit.len(), 1, "one Explicit device seed");
6300 assert_eq!(nodes.len(), 3, "three NodeVoltage seeds");
6301 let qb = nodes
6302 .iter()
6303 .find(|h| h.device_label == "Q1.base")
6304 .expect("Q1.base node seed");
6305 assert!(matches!(qb.state, InitState::NodeVoltage { v } if (v - 1.0308).abs() < 1e-6));
6306 }
6307
6308 #[test]
6309 fn parse_op_block_without_nodes_still_works() {
6310 let src = r#"pedal "test" {
6312 supply 24V
6313 components { Q1: npn(BC184C) }
6314 nets { in -> Q1.base Q1.collector -> out Q1.emitter -> gnd }
6315 controls {}
6316 op { Q1: { vbe: 0.64, vce: 5.35 } }
6317}"#;
6318 let def = parse_pedal_file(src).unwrap();
6319 assert_eq!(def.init_hints.len(), 1);
6320 assert!(matches!(def.init_hints[0].state, InitState::Explicit { .. }));
6321 }
6322}