1use crate::bytecode_encoder::encode_v1::encode_to_bytecode_v1;
6use crate::bytecode_encoder::encode_v2::{encode_composite_to_bytecode, encode_to_bytecode_v2};
7use crate::bytecode_encoder::error::BindRulesEncodeError;
8use crate::compiler::symbol_table::*;
9use crate::compiler::{dependency_graph, instruction};
10use crate::ddk_bind_constants::BIND_AUTOBIND;
11use crate::debugger::offline_debugger::AstLocation;
12use crate::errors::UserError;
13use crate::linter;
14use crate::parser::bind_rules::{self, Condition, ConditionOp, Statement};
15use crate::parser::common::{BindParserError, CompoundIdentifier, Value};
16use crate::parser::{self, bind_composite};
17use std::collections::HashMap;
18use std::fmt;
19use thiserror::Error;
20
21#[derive(Debug, Error, Clone, PartialEq)]
22pub enum CompilerError {
23 BindParserError(parser::common::BindParserError),
24 DependencyError(dependency_graph::DependencyError<CompoundIdentifier>),
25 LinterError(linter::LinterError),
26 DuplicateIdentifier(CompoundIdentifier),
27 TypeMismatch(CompoundIdentifier),
28 UnresolvedQualification(CompoundIdentifier),
29 UndeclaredKey(CompoundIdentifier),
30 MissingExtendsKeyword(CompoundIdentifier),
31 InvalidExtendsKeyword(CompoundIdentifier),
32 UnknownKey(CompoundIdentifier),
33 IfStatementMustBeTerminal,
34 TrueStatementMustBeIsolated,
35 FalseStatementMustBeIsolated,
36 MismatchedParentName { parent_name: String, property_name: String },
37}
38
39impl fmt::Display for CompilerError {
40 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41 write!(f, "{}", UserError::from(self.clone()))
42 }
43}
44
45#[derive(Debug, Error, Clone, PartialEq)]
46pub enum BindRulesDecodeError {
47 InvalidBinaryLength,
48}
49
50impl fmt::Display for BindRulesDecodeError {
51 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
52 write!(f, "{}", UserError::from(self.clone()))
53 }
54}
55
56#[derive(Debug, PartialEq)]
57pub enum CompiledBindRules<'a> {
58 Bind(BindRules<'a>),
59 CompositeBind(CompositeBindRules<'a>),
60}
61
62impl<'a> CompiledBindRules<'a> {
63 pub fn encode_to_bytecode(self) -> Result<Vec<u8>, BindRulesEncodeError> {
64 match self {
65 CompiledBindRules::Bind(bind_rules) => {
66 if bind_rules.use_new_bytecode {
67 return encode_to_bytecode_v2(bind_rules);
68 }
69
70 encode_to_bytecode_v1(bind_rules)
71 }
72 CompiledBindRules::CompositeBind(composite_bind) => {
73 encode_composite_to_bytecode(composite_bind)
74 }
75 }
76 }
77
78 pub fn empty_bind_rules(
79 use_new_bytecode: bool,
80 disable_autobind: bool,
81 enable_debug: bool,
82 ) -> CompiledBindRules<'a> {
83 let mut instructions = vec![];
84
85 if disable_autobind {
86 instructions.push(SymbolicInstructionInfo::disable_autobind());
87 }
88
89 if !use_new_bytecode {
90 instructions.push(SymbolicInstructionInfo {
91 location: None,
92 instruction: SymbolicInstruction::UnconditionalBind,
93 });
94 }
95
96 CompiledBindRules::Bind(BindRules {
97 instructions: instructions,
98 symbol_table: HashMap::new(),
99 use_new_bytecode: use_new_bytecode,
100 enable_debug: enable_debug,
101 })
102 }
103}
104
105#[derive(Debug, PartialEq)]
107pub struct BindRules<'a> {
108 pub symbol_table: SymbolTable,
109 pub instructions: Vec<SymbolicInstructionInfo<'a>>,
110 pub use_new_bytecode: bool,
111 pub enable_debug: bool,
112}
113
114#[derive(Clone, Debug, PartialEq)]
115pub enum SymbolicInstruction {
116 AbortIfEqual { lhs: Symbol, rhs: Symbol },
117 AbortIfNotEqual { lhs: Symbol, rhs: Symbol },
118 Label(u32),
119 UnconditionalJump { label: u32 },
120 JumpIfEqual { lhs: Symbol, rhs: Symbol, label: u32 },
121 JumpIfNotEqual { lhs: Symbol, rhs: Symbol, label: u32 },
122 UnconditionalAbort,
123 UnconditionalBind,
124}
125
126impl SymbolicInstruction {
127 pub fn to_instruction(self) -> instruction::Instruction {
128 match self {
129 SymbolicInstruction::AbortIfEqual { lhs, rhs } => {
130 instruction::Instruction::Abort(instruction::Condition::Equal(lhs, rhs))
131 }
132 SymbolicInstruction::AbortIfNotEqual { lhs, rhs } => {
133 instruction::Instruction::Abort(instruction::Condition::NotEqual(lhs, rhs))
134 }
135 SymbolicInstruction::Label(label_id) => instruction::Instruction::Label(label_id),
136 SymbolicInstruction::UnconditionalJump { label } => {
137 instruction::Instruction::Goto(instruction::Condition::Always, label)
138 }
139 SymbolicInstruction::JumpIfEqual { lhs, rhs, label } => {
140 instruction::Instruction::Goto(instruction::Condition::Equal(lhs, rhs), label)
141 }
142 SymbolicInstruction::JumpIfNotEqual { lhs, rhs, label } => {
143 instruction::Instruction::Goto(instruction::Condition::NotEqual(lhs, rhs), label)
144 }
145 SymbolicInstruction::UnconditionalAbort => {
146 instruction::Instruction::Abort(instruction::Condition::Always)
147 }
148 SymbolicInstruction::UnconditionalBind => {
149 instruction::Instruction::Match(instruction::Condition::Always)
150 }
151 }
152 }
153}
154
155#[derive(Clone, Debug, PartialEq)]
156pub struct SymbolicInstructionInfo<'a> {
157 pub location: Option<AstLocation<'a>>,
158 pub instruction: SymbolicInstruction,
159}
160
161impl<'a> SymbolicInstructionInfo<'a> {
162 pub fn to_instruction(self) -> instruction::InstructionInfo {
163 instruction::InstructionInfo {
164 instruction: self.instruction.to_instruction(),
165 debug: match self.location {
166 Some(location) => location.to_instruction_debug(),
167 None => instruction::InstructionDebug::none(),
168 },
169 }
170 }
171
172 pub fn disable_autobind() -> Self {
173 SymbolicInstructionInfo {
174 location: None,
175 instruction: SymbolicInstruction::AbortIfNotEqual {
176 lhs: Symbol::DeprecatedKey(BIND_AUTOBIND),
177 rhs: Symbol::NumberValue(0),
178 },
179 }
180 }
181}
182
183#[derive(Debug, PartialEq)]
184pub struct CompositeParent<'a> {
185 pub name: String,
186 pub instructions: Vec<SymbolicInstructionInfo<'a>>,
187}
188
189#[derive(Debug, PartialEq)]
190pub struct CompositeBindRules<'a> {
191 pub device_name: String,
192 pub symbol_table: SymbolTable,
193 pub primary_parent: CompositeParent<'a>,
194 pub additional_parents: Vec<CompositeParent<'a>>,
195 pub optional_parents: Vec<CompositeParent<'a>>,
196 pub enable_debug: bool,
197}
198
199pub fn compile<'a>(
200 rules_str: &'a str,
201 libraries: &[String],
202 lint: bool,
203 disable_autobind: bool,
204 use_new_bytecode: bool,
205 enable_debug: bool,
206) -> Result<CompiledBindRules<'a>, CompilerError> {
207 match bind_composite::Ast::try_from(rules_str) {
208 Ok(_) => {
209 return Ok(CompiledBindRules::CompositeBind(compile_bind_composite(
210 rules_str,
211 libraries,
212 lint,
213 use_new_bytecode,
214 enable_debug,
215 )?));
216 }
217 Err(BindParserError::CompositeKeyword(_)) => {
218 }
220 Err(e) => {
221 return Err(CompilerError::BindParserError(e));
222 }
223 }
224
225 Ok(CompiledBindRules::Bind(compile_bind(
226 rules_str,
227 libraries,
228 lint,
229 disable_autobind,
230 use_new_bytecode,
231 enable_debug,
232 )?))
233}
234
235pub fn compile_bind<'a>(
236 rules_str: &'a str,
237 libraries: &[String],
238 lint: bool,
239 disable_autobind: bool,
240 use_new_bytecode: bool,
241 enable_debug: bool,
242) -> Result<BindRules<'a>, CompilerError> {
243 let ast = bind_rules::Ast::try_from(rules_str).map_err(CompilerError::BindParserError)?;
244 let symbol_table = get_symbol_table_from_libraries(&ast.using, libraries, lint)?;
245
246 let mut instructions = compile_statements(ast.statements, &symbol_table, use_new_bytecode)?;
247 if disable_autobind {
248 instructions.insert(0, SymbolicInstructionInfo::disable_autobind());
249 }
250
251 Ok(BindRules {
252 symbol_table: symbol_table,
253 instructions: instructions,
254 use_new_bytecode: use_new_bytecode,
255 enable_debug: enable_debug,
256 })
257}
258
259pub fn compile_bind_composite<'a>(
260 rules_str: &'a str,
261 libraries: &[String],
262 lint: bool,
263 use_new_bytecode: bool,
264 enable_debug: bool,
265) -> Result<CompositeBindRules<'a>, CompilerError> {
266 let ast = bind_composite::Ast::try_from(rules_str).map_err(CompilerError::BindParserError)?;
267 let symbol_table = get_symbol_table_from_libraries(&ast.using, libraries, lint)?;
268
269 validate_parent_statements(
270 &ast.primary_parent.name,
271 &ast.primary_parent.statements,
272 &symbol_table,
273 )?;
274 for parent in &ast.additional_parents {
275 validate_parent_statements(&parent.name, &parent.statements, &symbol_table)?;
276 }
277 for parent in &ast.optional_parents {
278 validate_parent_statements(&parent.name, &parent.statements, &symbol_table)?;
279 }
280
281 let primary_parent = CompositeParent {
282 name: ast.primary_parent.name,
283 instructions: compile_statements(
284 ast.primary_parent.statements,
285 &symbol_table,
286 use_new_bytecode,
287 )?,
288 };
289 let additional_parents = ast
290 .additional_parents
291 .into_iter()
292 .map(|parent| {
293 let name = parent.name;
294 compile_statements(parent.statements, &symbol_table, use_new_bytecode)
295 .map(|inst| CompositeParent { name: name, instructions: inst })
296 })
297 .collect::<Result<Vec<CompositeParent<'_>>, CompilerError>>()?;
298
299 let optional_parents = ast
300 .optional_parents
301 .into_iter()
302 .map(|parent| {
303 let name = parent.name;
304 compile_statements(parent.statements, &symbol_table, use_new_bytecode)
305 .map(|inst| CompositeParent { name: name, instructions: inst })
306 })
307 .collect::<Result<Vec<CompositeParent<'_>>, CompilerError>>()?;
308
309 Ok(CompositeBindRules {
310 device_name: ast.name.to_string(),
311 symbol_table: symbol_table,
312 primary_parent: primary_parent,
313 additional_parents: additional_parents,
314 optional_parents: optional_parents,
315 enable_debug: enable_debug,
316 })
317}
318
319fn validate_parent_statements(
320 parent_name: &str,
321 statements: &[Statement<'_>],
322 symbol_table: &SymbolTable,
323) -> Result<(), CompilerError> {
324 for statement in statements {
325 match statement {
326 Statement::ConditionStatement { condition, .. } => {
327 validate_parent_condition(parent_name, condition, symbol_table)?;
328 }
329 Statement::Accept { identifier, values, .. } => {
330 if is_fuchsia_name_key(identifier, symbol_table)
331 && !values.iter().any(|v| get_value_string(v, symbol_table) == parent_name)
332 {
333 let prop_name = values
334 .first()
335 .map(|v| get_value_string(v, symbol_table))
336 .unwrap_or_default();
337 return Err(CompilerError::MismatchedParentName {
338 parent_name: parent_name.to_string(),
339 property_name: prop_name,
340 });
341 }
342 }
343 Statement::If { blocks, else_block, .. } => {
344 for (condition, block_statements) in blocks {
345 validate_parent_condition(parent_name, condition, symbol_table)?;
346 validate_parent_statements(parent_name, block_statements, symbol_table)?;
347 }
348 validate_parent_statements(parent_name, else_block, symbol_table)?;
349 }
350 Statement::True { .. } | Statement::False { .. } => {}
351 }
352 }
353 Ok(())
354}
355
356fn validate_parent_condition(
357 parent_name: &str,
358 condition: &Condition<'_>,
359 symbol_table: &SymbolTable,
360) -> Result<(), CompilerError> {
361 if is_fuchsia_name_key(&condition.lhs, symbol_table) && condition.op == ConditionOp::Equals {
362 let prop_name = get_value_string(&condition.rhs, symbol_table);
363 if prop_name != parent_name {
364 return Err(CompilerError::MismatchedParentName {
365 parent_name: parent_name.to_string(),
366 property_name: prop_name,
367 });
368 }
369 }
370 Ok(())
371}
372
373fn is_fuchsia_name_key(ident: &CompoundIdentifier, symbol_table: &SymbolTable) -> bool {
374 if let Some(Symbol::Key(key, _)) = symbol_table.get(ident) {
375 if key == "fuchsia.NAME" {
376 return true;
377 }
378 }
379 ident.to_string() == "fuchsia.NAME" || ident.to_string() == "NAME"
380}
381
382fn get_value_string(value: &Value, symbol_table: &SymbolTable) -> String {
383 match value {
384 Value::StringLiteral(s) => s.clone(),
385 Value::Identifier(ident) => {
386 if let Some(Symbol::StringValue(s)) = symbol_table.get(ident) {
387 s.clone()
388 } else {
389 ident.to_string()
390 }
391 }
392 Value::NumericLiteral(n) => n.to_string(),
393 Value::BoolLiteral(b) => b.to_string(),
394 }
395}
396
397pub fn compile_statements<'a, 'b>(
398 statements: Vec<Statement<'a>>,
399 symbol_table: &'b SymbolTable,
400 use_new_bytecode: bool,
401) -> Result<Vec<SymbolicInstructionInfo<'a>>, CompilerError> {
402 let mut compiler = Compiler::new(symbol_table);
403 compiler.compile_statements(statements, use_new_bytecode)?;
404 Ok(compiler.instructions)
405}
406
407struct Compiler<'a, 'b> {
408 symbol_table: &'b SymbolTable,
409 pub instructions: Vec<SymbolicInstructionInfo<'a>>,
410 next_label_id: u32,
411}
412
413impl<'a, 'b> Compiler<'a, 'b> {
414 fn new(symbol_table: &'b SymbolTable) -> Self {
415 Compiler { symbol_table: symbol_table, instructions: vec![], next_label_id: 0 }
416 }
417
418 fn lookup_identifier(&self, identifier: &CompoundIdentifier) -> Result<Symbol, CompilerError> {
419 let symbol = self
420 .symbol_table
421 .get(identifier)
422 .ok_or_else(|| CompilerError::UnknownKey(identifier.clone()))?;
423 Ok(symbol.clone())
424 }
425
426 fn lookup_value(&self, value: &Value) -> Result<Symbol, CompilerError> {
427 match value {
428 Value::NumericLiteral(n) => Ok(Symbol::NumberValue(*n)),
429 Value::StringLiteral(s) => Ok(Symbol::StringValue(s.to_string())),
430 Value::BoolLiteral(b) => Ok(Symbol::BoolValue(*b)),
431 Value::Identifier(ident) => self
432 .symbol_table
433 .get(ident)
434 .ok_or_else(|| CompilerError::UnknownKey(ident.clone()))
435 .map(|x| x.clone()),
436 }
437 }
438
439 fn compile_statements(
440 &mut self,
441 statements: Vec<Statement<'a>>,
442 use_new_bytecode: bool,
443 ) -> Result<(), CompilerError> {
444 self.compile_block(statements)?;
445
446 if !use_new_bytecode {
448 self.instructions.push(SymbolicInstructionInfo {
449 location: None,
450 instruction: SymbolicInstruction::UnconditionalBind,
451 });
452 }
453
454 Ok(())
455 }
456
457 fn get_unique_label(&mut self) -> u32 {
458 let label = self.next_label_id;
459 self.next_label_id += 1;
460 label
461 }
462
463 fn compile_block(&mut self, statements: Vec<Statement<'a>>) -> Result<(), CompilerError> {
464 let num_statements = statements.len();
465 let mut iter = statements.into_iter().peekable();
466 while let Some(statement) = iter.next() {
467 match statement {
468 Statement::ConditionStatement { .. } => {
469 if let Statement::ConditionStatement {
470 span: _,
471 condition: Condition { span: _, lhs, op, rhs },
472 } = &statement
473 {
474 let lhs_symbol = self.lookup_identifier(lhs)?;
475 let rhs_symbol = self.lookup_value(rhs)?;
476 let instruction = match op {
477 ConditionOp::Equals => SymbolicInstruction::AbortIfNotEqual {
478 lhs: lhs_symbol,
479 rhs: rhs_symbol,
480 },
481 ConditionOp::NotEquals => SymbolicInstruction::AbortIfEqual {
482 lhs: lhs_symbol,
483 rhs: rhs_symbol,
484 },
485 };
486 self.instructions.push(SymbolicInstructionInfo {
487 location: Some(AstLocation::ConditionStatement(statement)),
488 instruction,
489 });
490 }
491 }
492 Statement::Accept { span, identifier, values } => {
493 let lhs_symbol = self.lookup_identifier(&identifier)?;
494 let label_id = self.get_unique_label();
495 for value in values {
496 self.instructions.push(SymbolicInstructionInfo {
497 location: Some(AstLocation::AcceptStatementValue {
498 identifier: identifier.clone(),
499 value: value.clone(),
500 span: span.clone(),
501 }),
502 instruction: SymbolicInstruction::JumpIfEqual {
503 lhs: lhs_symbol.clone(),
504 rhs: self.lookup_value(&value)?,
505 label: label_id,
506 },
507 });
508 }
509 self.instructions.push(SymbolicInstructionInfo {
510 location: Some(AstLocation::AcceptStatementFailure {
511 identifier,
512 symbol: lhs_symbol,
513 span,
514 }),
515 instruction: SymbolicInstruction::UnconditionalAbort,
516 });
517 self.instructions.push(SymbolicInstructionInfo {
518 location: None,
519 instruction: SymbolicInstruction::Label(label_id),
520 });
521 }
522 Statement::If { span: _, blocks, else_block } => {
523 if !iter.peek().is_none() {
524 return Err(CompilerError::IfStatementMustBeTerminal);
525 }
526
527 let final_label_id = self.get_unique_label();
528
529 for (condition, block_statements) in blocks {
530 let Condition { span: _, lhs, op, rhs } = &condition;
531
532 let lhs_symbol = self.lookup_identifier(lhs)?;
533 let rhs_symbol = self.lookup_value(rhs)?;
534
535 let label_id = self.get_unique_label();
537 let instruction = match op {
538 ConditionOp::Equals => SymbolicInstruction::JumpIfNotEqual {
539 lhs: lhs_symbol,
540 rhs: rhs_symbol,
541 label: label_id,
542 },
543 ConditionOp::NotEquals => SymbolicInstruction::JumpIfEqual {
544 lhs: lhs_symbol,
545 rhs: rhs_symbol,
546 label: label_id,
547 },
548 };
549 self.instructions.push(SymbolicInstructionInfo {
550 location: Some(AstLocation::IfCondition(condition)),
551 instruction,
552 });
553
554 self.compile_block(block_statements)?;
556
557 self.instructions.push(SymbolicInstructionInfo {
559 location: None,
560 instruction: SymbolicInstruction::UnconditionalJump {
561 label: final_label_id,
562 },
563 });
564
565 self.instructions.push(SymbolicInstructionInfo {
567 location: None,
568 instruction: SymbolicInstruction::Label(label_id),
569 });
570 }
571
572 self.compile_block(else_block)?;
574
575 self.instructions.push(SymbolicInstructionInfo {
581 location: None,
582 instruction: SymbolicInstruction::Label(final_label_id),
583 });
584 }
585 Statement::False { span: _ } => {
586 if num_statements != 1 {
587 return Err(CompilerError::FalseStatementMustBeIsolated);
588 }
589 self.instructions.push(SymbolicInstructionInfo {
590 location: Some(AstLocation::FalseStatement(statement)),
591 instruction: SymbolicInstruction::UnconditionalAbort,
592 });
593 }
594 Statement::True { .. } => {
595 if num_statements != 1 {
597 return Err(CompilerError::TrueStatementMustBeIsolated);
598 }
599 }
600 }
601 }
602 Ok(())
603 }
604}
605
606#[cfg(test)]
607mod test {
608 use super::*;
609 use crate::make_identifier;
610 use crate::parser::bind_library;
611 use crate::parser::common::{Include, Span};
612
613 #[test]
614 fn condition() {
615 let condition_statement = Statement::ConditionStatement {
616 span: Span::new(),
617 condition: Condition {
618 span: Span::new(),
619 lhs: make_identifier!("abc"),
620 op: ConditionOp::Equals,
621 rhs: Value::NumericLiteral(42),
622 },
623 };
624
625 let rules =
626 bind_rules::Ast { using: vec![], statements: vec![condition_statement.clone()] };
627 let mut symbol_table = HashMap::new();
628 symbol_table.insert(
629 make_identifier!("abc"),
630 Symbol::Key("abc".to_string(), bind_library::ValueType::Number),
631 );
632
633 assert_eq!(
634 compile_statements(rules.statements, &symbol_table, false).unwrap(),
635 vec![
636 SymbolicInstructionInfo {
637 location: Some(AstLocation::ConditionStatement(condition_statement)),
638 instruction: SymbolicInstruction::AbortIfNotEqual {
639 lhs: Symbol::Key("abc".to_string(), bind_library::ValueType::Number),
640 rhs: Symbol::NumberValue(42)
641 }
642 },
643 SymbolicInstructionInfo {
644 location: None,
645 instruction: SymbolicInstruction::UnconditionalBind
646 }
647 ]
648 );
649 }
650
651 #[test]
652 fn accept() {
653 let rules = bind_rules::Ast {
654 using: vec![],
655 statements: vec![Statement::Accept {
656 span: Span::new(),
657 identifier: make_identifier!("abc"),
658 values: vec![Value::NumericLiteral(42), Value::NumericLiteral(314)],
659 }],
660 };
661 let mut symbol_table = HashMap::new();
662 symbol_table.insert(
663 make_identifier!("abc"),
664 Symbol::Key("abc".to_string(), bind_library::ValueType::Number),
665 );
666
667 assert_eq!(
668 compile_statements(rules.statements, &symbol_table, false).unwrap(),
669 vec![
670 SymbolicInstructionInfo {
671 location: Some(AstLocation::AcceptStatementValue {
672 identifier: make_identifier!("abc"),
673 value: Value::NumericLiteral(42),
674 span: Span::new()
675 }),
676 instruction: SymbolicInstruction::JumpIfEqual {
677 lhs: Symbol::Key("abc".to_string(), bind_library::ValueType::Number),
678 rhs: Symbol::NumberValue(42),
679 label: 0
680 }
681 },
682 SymbolicInstructionInfo {
683 location: Some(AstLocation::AcceptStatementValue {
684 identifier: make_identifier!("abc"),
685 value: Value::NumericLiteral(314),
686 span: Span::new()
687 }),
688 instruction: SymbolicInstruction::JumpIfEqual {
689 lhs: Symbol::Key("abc".to_string(), bind_library::ValueType::Number),
690 rhs: Symbol::NumberValue(314),
691 label: 0
692 }
693 },
694 SymbolicInstructionInfo {
695 location: Some(AstLocation::AcceptStatementFailure {
696 identifier: make_identifier!("abc"),
697 symbol: Symbol::Key("abc".to_string(), bind_library::ValueType::Number),
698 span: Span::new()
699 }),
700 instruction: SymbolicInstruction::UnconditionalAbort
701 },
702 SymbolicInstructionInfo {
703 location: None,
704 instruction: SymbolicInstruction::Label(0)
705 },
706 SymbolicInstructionInfo {
707 location: None,
708 instruction: SymbolicInstruction::UnconditionalBind
709 },
710 ]
711 );
712 }
713
714 #[test]
715 fn if_else() {
716 let condition1 = Condition {
717 span: Span::new(),
718 lhs: make_identifier!("abc"),
719 op: ConditionOp::Equals,
720 rhs: Value::NumericLiteral(1),
721 };
722 let condition2 = Condition {
723 span: Span::new(),
724 lhs: make_identifier!("abc"),
725 op: ConditionOp::Equals,
726 rhs: Value::NumericLiteral(2),
727 };
728 let statement1 = Statement::ConditionStatement {
729 span: Span::new(),
730 condition: Condition {
731 span: Span::new(),
732 lhs: make_identifier!("abc"),
733 op: ConditionOp::Equals,
734 rhs: Value::NumericLiteral(2),
735 },
736 };
737 let statement2 = Statement::ConditionStatement {
738 span: Span::new(),
739 condition: Condition {
740 span: Span::new(),
741 lhs: make_identifier!("abc"),
742 op: ConditionOp::Equals,
743 rhs: Value::NumericLiteral(3),
744 },
745 };
746 let statement3 = Statement::ConditionStatement {
747 span: Span::new(),
748 condition: Condition {
749 span: Span::new(),
750 lhs: make_identifier!("abc"),
751 op: ConditionOp::Equals,
752 rhs: Value::NumericLiteral(3),
753 },
754 };
755
756 let rules = bind_rules::Ast {
757 using: vec![],
758 statements: vec![Statement::If {
759 span: Span::new(),
760 blocks: vec![
761 (condition1.clone(), vec![statement1.clone()]),
762 (condition2.clone(), vec![statement2.clone()]),
763 ],
764 else_block: vec![statement3.clone()],
765 }],
766 };
767 let mut symbol_table = HashMap::new();
768 symbol_table.insert(
769 make_identifier!("abc"),
770 Symbol::Key("abc".to_string(), bind_library::ValueType::Number),
771 );
772
773 assert_eq!(
774 compile_statements(rules.statements, &symbol_table, false).unwrap(),
775 vec![
776 SymbolicInstructionInfo {
777 location: Some(AstLocation::IfCondition(condition1)),
778 instruction: SymbolicInstruction::JumpIfNotEqual {
779 lhs: Symbol::Key("abc".to_string(), bind_library::ValueType::Number),
780 rhs: Symbol::NumberValue(1),
781 label: 1
782 }
783 },
784 SymbolicInstructionInfo {
785 location: Some(AstLocation::ConditionStatement(statement1)),
786 instruction: SymbolicInstruction::AbortIfNotEqual {
787 lhs: Symbol::Key("abc".to_string(), bind_library::ValueType::Number),
788 rhs: Symbol::NumberValue(2)
789 }
790 },
791 SymbolicInstructionInfo {
792 location: None,
793 instruction: SymbolicInstruction::UnconditionalJump { label: 0 }
794 },
795 SymbolicInstructionInfo {
796 location: None,
797 instruction: SymbolicInstruction::Label(1)
798 },
799 SymbolicInstructionInfo {
800 location: Some(AstLocation::IfCondition(condition2)),
801 instruction: SymbolicInstruction::JumpIfNotEqual {
802 lhs: Symbol::Key("abc".to_string(), bind_library::ValueType::Number),
803 rhs: Symbol::NumberValue(2),
804 label: 2
805 }
806 },
807 SymbolicInstructionInfo {
808 location: Some(AstLocation::ConditionStatement(statement2)),
809 instruction: SymbolicInstruction::AbortIfNotEqual {
810 lhs: Symbol::Key("abc".to_string(), bind_library::ValueType::Number),
811 rhs: Symbol::NumberValue(3)
812 }
813 },
814 SymbolicInstructionInfo {
815 location: None,
816 instruction: SymbolicInstruction::UnconditionalJump { label: 0 }
817 },
818 SymbolicInstructionInfo {
819 location: None,
820 instruction: SymbolicInstruction::Label(2)
821 },
822 SymbolicInstructionInfo {
823 location: Some(AstLocation::ConditionStatement(statement3)),
824 instruction: SymbolicInstruction::AbortIfNotEqual {
825 lhs: Symbol::Key("abc".to_string(), bind_library::ValueType::Number),
826 rhs: Symbol::NumberValue(3)
827 }
828 },
829 SymbolicInstructionInfo {
830 location: None,
831 instruction: SymbolicInstruction::Label(0)
832 },
833 SymbolicInstructionInfo {
834 location: None,
835 instruction: SymbolicInstruction::UnconditionalBind
836 },
837 ]
838 );
839 }
840
841 #[test]
842 fn if_else_must_be_terminal() {
843 let rules = bind_rules::Ast {
844 using: vec![],
845 statements: vec![
846 Statement::If {
847 span: Span::new(),
848 blocks: vec![(
849 Condition {
850 span: Span::new(),
851 lhs: make_identifier!("abc"),
852 op: ConditionOp::Equals,
853 rhs: Value::NumericLiteral(1),
854 },
855 vec![Statement::ConditionStatement {
856 span: Span::new(),
857 condition: Condition {
858 span: Span::new(),
859 lhs: make_identifier!("abc"),
860 op: ConditionOp::Equals,
861 rhs: Value::NumericLiteral(2),
862 },
863 }],
864 )],
865 else_block: vec![Statement::ConditionStatement {
866 span: Span::new(),
867 condition: Condition {
868 span: Span::new(),
869 lhs: make_identifier!("abc"),
870 op: ConditionOp::Equals,
871 rhs: Value::NumericLiteral(3),
872 },
873 }],
874 },
875 Statement::Accept {
876 span: Span::new(),
877 identifier: make_identifier!("abc"),
878 values: vec![Value::NumericLiteral(42), Value::NumericLiteral(314)],
879 },
880 ],
881 };
882 let mut symbol_table = HashMap::new();
883 symbol_table.insert(
884 make_identifier!("abc"),
885 Symbol::Key("abc".to_string(), bind_library::ValueType::Number),
886 );
887
888 assert_eq!(
889 compile_statements(rules.statements, &symbol_table, false),
890 Err(CompilerError::IfStatementMustBeTerminal)
891 );
892 }
893
894 #[test]
895 fn false_statement() {
896 let abort_statement = Statement::False { span: Span::new() };
897
898 let rules = bind_rules::Ast { using: vec![], statements: vec![abort_statement.clone()] };
899 let symbol_table = HashMap::new();
900
901 assert_eq!(
902 compile_statements(rules.statements, &symbol_table, false).unwrap(),
903 vec![
904 SymbolicInstructionInfo {
905 location: Some(AstLocation::FalseStatement(abort_statement)),
906 instruction: SymbolicInstruction::UnconditionalAbort
907 },
908 SymbolicInstructionInfo {
909 location: None,
910 instruction: SymbolicInstruction::UnconditionalBind
911 }
912 ]
913 );
914 }
915
916 #[test]
917 fn false_statement_must_be_isolated() {
918 let condition_statement = Statement::ConditionStatement {
919 span: Span::new(),
920 condition: Condition {
921 span: Span::new(),
922 lhs: make_identifier!("abc"),
923 op: ConditionOp::Equals,
924 rhs: Value::NumericLiteral(42),
925 },
926 };
927 let abort_statement = Statement::False { span: Span::new() };
928
929 let rules = bind_rules::Ast {
930 using: vec![],
931 statements: vec![condition_statement.clone(), abort_statement.clone()],
932 };
933 let mut symbol_table = HashMap::new();
934 symbol_table.insert(
935 make_identifier!("abc"),
936 Symbol::Key("abc".to_string(), bind_library::ValueType::Number),
937 );
938
939 assert_eq!(
940 compile_statements(rules.statements, &symbol_table, false),
941 Err(CompilerError::FalseStatementMustBeIsolated)
942 );
943 }
944
945 #[test]
946 fn true_statement_must_be_isolated() {
947 let condition_statement = Statement::ConditionStatement {
948 span: Span::new(),
949 condition: Condition {
950 span: Span::new(),
951 lhs: make_identifier!("abc"),
952 op: ConditionOp::Equals,
953 rhs: Value::NumericLiteral(42),
954 },
955 };
956 let abort_statement = Statement::True { span: Span::new() };
957
958 let rules = bind_rules::Ast {
959 using: vec![],
960 statements: vec![condition_statement.clone(), abort_statement.clone()],
961 };
962 let mut symbol_table = HashMap::new();
963 symbol_table.insert(
964 make_identifier!("abc"),
965 Symbol::Key("abc".to_string(), bind_library::ValueType::Number),
966 );
967
968 assert_eq!(
969 compile_statements(rules.statements, &symbol_table, false),
970 Err(CompilerError::TrueStatementMustBeIsolated)
971 );
972 }
973
974 #[test]
975 fn dependencies() {
976 let rules = bind_rules::Ast {
977 using: vec![Include { name: make_identifier!("A"), alias: None }],
978 statements: vec![],
979 };
980 let libraries = vec![
981 bind_library::Ast {
982 name: make_identifier!("A"),
983 using: vec![Include { name: make_identifier!("A", "B"), alias: None }],
984 declarations: vec![],
985 },
986 bind_library::Ast {
987 name: make_identifier!("A", "B"),
988 using: vec![],
989 declarations: vec![],
990 },
991 bind_library::Ast {
992 name: make_identifier!("A", "C"),
993 using: vec![],
994 declarations: vec![],
995 },
996 ];
997
998 let mut resolved = resolve_dependencies(&rules.using, libraries.iter()).unwrap();
999 resolved.sort_by_key(|lib| lib.name.to_string());
1000
1001 assert_eq!(
1002 resolved,
1003 vec![
1004 &bind_library::Ast {
1005 name: make_identifier!("A"),
1006 using: vec![Include { name: make_identifier!("A", "B"), alias: None }],
1007 declarations: vec![],
1008 },
1009 &bind_library::Ast {
1010 name: make_identifier!("A", "B"),
1011 using: vec![],
1012 declarations: vec![],
1013 },
1014 &bind_library::Ast {
1015 name: make_identifier!("A", "C"),
1016 using: vec![],
1017 declarations: vec![],
1018 },
1019 ]
1020 );
1021 }
1022
1023 #[test]
1024 fn dependencies_error() {
1025 let rules = bind_rules::Ast {
1026 using: vec![Include { name: make_identifier!("A"), alias: None }],
1027 statements: vec![],
1028 };
1029 let libraries = vec![
1030 bind_library::Ast {
1031 name: make_identifier!("A"),
1032 using: vec![Include { name: make_identifier!("A", "B"), alias: None }],
1033 declarations: vec![],
1034 },
1035 bind_library::Ast {
1036 name: make_identifier!("A", "C"),
1037 using: vec![],
1038 declarations: vec![],
1039 },
1040 ];
1041
1042 assert_eq!(
1043 resolve_dependencies(&rules.using, libraries.iter()),
1044 Err(CompilerError::DependencyError(
1045 dependency_graph::DependencyError::MissingDependency(make_identifier!("A", "B"))
1046 ))
1047 );
1048 }
1049
1050 #[test]
1051 fn uncondition_bind_in_new_bytecode() {
1052 let condition_statement = Statement::ConditionStatement {
1053 span: Span::new(),
1054 condition: Condition {
1055 span: Span::new(),
1056 lhs: make_identifier!("wheatear"),
1057 op: ConditionOp::Equals,
1058 rhs: Value::NumericLiteral(8),
1059 },
1060 };
1061
1062 let rules =
1063 bind_rules::Ast { using: vec![], statements: vec![condition_statement.clone()] };
1064 let mut symbol_table = HashMap::new();
1065 symbol_table.insert(
1066 make_identifier!("wheatear"),
1067 Symbol::Key("wheatear".to_string(), bind_library::ValueType::Number),
1068 );
1069
1070 assert_eq!(
1072 compile_statements(rules.statements, &symbol_table, true).unwrap(),
1073 vec![SymbolicInstructionInfo {
1074 location: Some(AstLocation::ConditionStatement(condition_statement)),
1075 instruction: SymbolicInstruction::AbortIfNotEqual {
1076 lhs: Symbol::Key("wheatear".to_string(), bind_library::ValueType::Number),
1077 rhs: Symbol::NumberValue(8)
1078 }
1079 },]
1080 );
1081 }
1082
1083 #[test]
1084 fn composite_matching_parent_names() {
1085 let fuchsia_lib = "library fuchsia;\nstring NAME;\n";
1086 let libraries = vec![fuchsia_lib.to_string()];
1087 let rules = "
1088 composite test_device;
1089 using fuchsia;
1090
1091 primary parent \"primary_node\" {
1092 fuchsia.NAME == \"primary_node\";
1093 }
1094
1095 parent \"additional_node\" {
1096 fuchsia.NAME == \"additional_node\";
1097 }
1098
1099 optional parent \"optional_node\" {
1100 fuchsia.NAME == \"optional_node\";
1101 }
1102 ";
1103
1104 assert!(compile_bind_composite(rules, &libraries, false, false, false).is_ok());
1105 }
1106
1107 #[test]
1108 fn composite_mismatched_primary_parent_name() {
1109 let fuchsia_lib = "library fuchsia;\nstring NAME;\n";
1110 let libraries = vec![fuchsia_lib.to_string()];
1111 let rules = "
1112 composite test_device;
1113 using fuchsia;
1114
1115 primary parent \"primary_node\" {
1116 fuchsia.NAME == \"wrong_name\";
1117 }
1118 ";
1119
1120 assert_eq!(
1121 compile_bind_composite(rules, &libraries, false, false, false),
1122 Err(CompilerError::MismatchedParentName {
1123 parent_name: "primary_node".to_string(),
1124 property_name: "wrong_name".to_string(),
1125 })
1126 );
1127 }
1128
1129 #[test]
1130 fn composite_mismatched_additional_parent_name() {
1131 let fuchsia_lib = "library fuchsia;\nstring NAME;\n";
1132 let libraries = vec![fuchsia_lib.to_string()];
1133 let rules = "
1134 composite test_device;
1135 using fuchsia;
1136
1137 primary parent \"primary_node\" {
1138 fuchsia.NAME == \"primary_node\";
1139 }
1140
1141 parent \"node_b\" {
1142 fuchsia.NAME == \"wrong_b\";
1143 }
1144 ";
1145
1146 assert_eq!(
1147 compile_bind_composite(rules, &libraries, false, false, false),
1148 Err(CompilerError::MismatchedParentName {
1149 parent_name: "node_b".to_string(),
1150 property_name: "wrong_b".to_string(),
1151 })
1152 );
1153 }
1154
1155 #[test]
1156 fn composite_mismatched_optional_parent_name() {
1157 let fuchsia_lib = "library fuchsia;\nstring NAME;\n";
1158 let libraries = vec![fuchsia_lib.to_string()];
1159 let rules = "
1160 composite test_device;
1161 using fuchsia;
1162
1163 primary parent \"primary_node\" {
1164 fuchsia.NAME == \"primary_node\";
1165 }
1166
1167 optional parent \"opt_node\" {
1168 fuchsia.NAME == \"wrong_opt\";
1169 }
1170 ";
1171
1172 assert_eq!(
1173 compile_bind_composite(rules, &libraries, false, false, false),
1174 Err(CompilerError::MismatchedParentName {
1175 parent_name: "opt_node".to_string(),
1176 property_name: "wrong_opt".to_string(),
1177 })
1178 );
1179 }
1180
1181 #[test]
1182 fn composite_accept_parent_name() {
1183 let fuchsia_lib = "library fuchsia;\nstring NAME;\n";
1184 let libraries = vec![fuchsia_lib.to_string()];
1185 let rules = "
1186 composite test_device;
1187 using fuchsia;
1188
1189 primary parent \"primary_node\" {
1190 accept fuchsia.NAME { \"primary_node\", \"other_name\" }
1191 }
1192 ";
1193
1194 assert!(compile_bind_composite(rules, &libraries, false, false, false).is_ok());
1195 }
1196
1197 #[test]
1198 fn composite_mismatched_accept_parent_name() {
1199 let fuchsia_lib = "library fuchsia;\nstring NAME;\n";
1200 let libraries = vec![fuchsia_lib.to_string()];
1201 let rules = "
1202 composite test_device;
1203 using fuchsia;
1204
1205 primary parent \"primary_node\" {
1206 accept fuchsia.NAME { \"other_name\", \"another_name\" }
1207 }
1208 ";
1209
1210 assert_eq!(
1211 compile_bind_composite(rules, &libraries, false, false, false),
1212 Err(CompilerError::MismatchedParentName {
1213 parent_name: "primary_node".to_string(),
1214 property_name: "other_name".to_string(),
1215 })
1216 );
1217 }
1218
1219 #[test]
1220 fn composite_mismatched_if_parent_name() {
1221 let fuchsia_lib = "library fuchsia;\nstring NAME;\nuint PROTOCOL;\n";
1222 let libraries = vec![fuchsia_lib.to_string()];
1223 let rules = "
1224 composite test_device;
1225 using fuchsia;
1226
1227 primary parent \"primary_node\" {
1228 if fuchsia.PROTOCOL == 1 {
1229 fuchsia.NAME == \"wrong_name\";
1230 } else {
1231 fuchsia.NAME == \"primary_node\";
1232 }
1233 }
1234 ";
1235
1236 assert_eq!(
1237 compile_bind_composite(rules, &libraries, false, false, false),
1238 Err(CompilerError::MismatchedParentName {
1239 parent_name: "primary_node".to_string(),
1240 property_name: "wrong_name".to_string(),
1241 })
1242 );
1243 }
1244}