Skip to main content

bind/compiler/
compiler.rs

1// Copyright 2019 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use 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// TODO(https://fxbug.dev/42054389): Use enable debug flag.
106#[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            /* Fall through for non-composite bind rules */
219        }
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 none of the statements caused an abort, then we should bind the driver.
447        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                        // Generate instructions for the condition.
536                        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                        // Compile the block itself.
555                        self.compile_block(block_statements)?;
556
557                        // Jump to after the if statement.
558                        self.instructions.push(SymbolicInstructionInfo {
559                            location: None,
560                            instruction: SymbolicInstruction::UnconditionalJump {
561                                label: final_label_id,
562                            },
563                        });
564
565                        // Insert a label to jump to when the condition fails.
566                        self.instructions.push(SymbolicInstructionInfo {
567                            location: None,
568                            instruction: SymbolicInstruction::Label(label_id),
569                        });
570                    }
571
572                    // Compile the else block.
573                    self.compile_block(else_block)?;
574
575                    // Insert a label to jump to at the end of the whole if statement. Note that we
576                    // could just emit an unconditional bind instead of jumping, since we know that
577                    // if statements are terminal, but we do the jump to be consistent with
578                    // condition and accept statements.
579
580                    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                    // A `true` statement doesn't require an instruction.
596                    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        // Instructions should not contain unconditional bind.
1071        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}