bind/debugger/
offline_debugger.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_symbol;
6use crate::compiler::instruction::{InstructionDebug, RawAstLocation};
7use crate::compiler::{
8    self, BindRules, Symbol, SymbolTable, SymbolicInstruction, SymbolicInstructionInfo,
9};
10use crate::debugger::device_specification::{DeviceSpecification, Property};
11use crate::errors::UserError;
12use crate::parser::bind_rules::{Condition, ConditionOp, Statement};
13use crate::parser::common::{BindParserError, CompoundIdentifier, Span, Value};
14use std::collections::{HashMap, HashSet};
15use std::fmt;
16use std::str::FromStr;
17use thiserror::Error;
18
19#[derive(Debug, Error, Clone, PartialEq)]
20pub enum DebuggerError {
21    ParserError(BindParserError),
22    CompilerError(compiler::CompilerError),
23    DuplicateKey(CompoundIdentifier),
24    MissingLabel,
25    NoOutcome,
26    IncorrectAstLocation,
27    InvalidAstLocation,
28    UnknownKey(CompoundIdentifier),
29    InvalidValueSymbol(Symbol),
30}
31
32impl fmt::Display for DebuggerError {
33    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34        write!(f, "{}", UserError::from(self.clone()))
35    }
36}
37
38type DevicePropertyMap = HashMap<Symbol, DeviceValue>;
39
40#[derive(Debug, PartialEq)]
41struct DeviceValue {
42    symbol: Option<Symbol>,
43    identifier: Option<CompoundIdentifier>,
44}
45
46#[derive(Debug, Clone, PartialEq)]
47pub enum AstLocation<'a> {
48    ConditionStatement(Statement<'a>),
49    AcceptStatementValue { identifier: CompoundIdentifier, value: Value, span: Span<'a> },
50    AcceptStatementFailure { identifier: CompoundIdentifier, symbol: Symbol, span: Span<'a> },
51    IfCondition(Condition<'a>),
52    FalseStatement(Statement<'a>),
53}
54
55impl<'a> AstLocation<'a> {
56    pub fn to_instruction_debug(self) -> InstructionDebug {
57        match self {
58            AstLocation::ConditionStatement(statement) => InstructionDebug {
59                line: statement.get_span().line,
60                ast_location: RawAstLocation::ConditionStatement,
61                extra: 0,
62            },
63            AstLocation::AcceptStatementValue { span, .. } => InstructionDebug {
64                line: span.line,
65                ast_location: RawAstLocation::AcceptStatementValue,
66                extra: 0,
67            },
68            AstLocation::AcceptStatementFailure { span, symbol, .. } => InstructionDebug {
69                line: span.line,
70                ast_location: RawAstLocation::AcceptStatementFailure,
71                extra: encode_symbol(symbol).unwrap_or(0),
72            },
73            AstLocation::IfCondition(condition) => InstructionDebug {
74                line: condition.span.line,
75                ast_location: RawAstLocation::IfCondition,
76                extra: 0,
77            },
78            AstLocation::FalseStatement(statement) => InstructionDebug {
79                line: statement.get_span().line,
80                ast_location: RawAstLocation::FalseStatement,
81                extra: 0,
82            },
83        }
84    }
85}
86
87#[derive(Debug, PartialEq)]
88enum DebuggerOutput<'a> {
89    ConditionStatement {
90        statement: &'a Statement<'a>,
91        success: bool,
92    },
93    FalseStatement {
94        statement: &'a Statement<'a>,
95    },
96    AcceptStatementSuccess {
97        identifier: &'a CompoundIdentifier,
98        value: &'a Value,
99        value_symbol: &'a Symbol,
100        span: &'a Span<'a>,
101    },
102    AcceptStatementFailure {
103        identifier: &'a CompoundIdentifier,
104        span: &'a Span<'a>,
105    },
106    IfCondition {
107        condition: &'a Condition<'a>,
108        success: bool,
109    },
110}
111
112pub fn debug_from_str<'a>(
113    bind_rules: &BindRules,
114    device_file: &str,
115) -> Result<bool, DebuggerError> {
116    let device_specification =
117        DeviceSpecification::from_str(device_file).map_err(DebuggerError::ParserError)?;
118
119    debug_from_device_specification(
120        &bind_rules.symbol_table,
121        &bind_rules.instructions,
122        device_specification,
123    )
124}
125
126pub fn debug_from_device_specification<'a>(
127    symbol_table: &SymbolTable,
128    instructions: &Vec<SymbolicInstructionInfo<'a>>,
129    device_specification: DeviceSpecification,
130) -> Result<bool, DebuggerError> {
131    let mut debugger = Debugger::new(&device_specification.properties, symbol_table, instructions)?;
132    let binds = debugger.evaluate_bind_rules()?;
133    debugger.log_output()?;
134    Ok(binds)
135}
136
137struct Debugger<'a> {
138    device_properties: DevicePropertyMap,
139    symbol_table: &'a SymbolTable,
140    instructions: &'a Vec<SymbolicInstructionInfo<'a>>,
141    output: Vec<DebuggerOutput<'a>>,
142}
143
144impl<'a> Debugger<'a> {
145    fn new(
146        properties: &[Property],
147        symbol_table: &'a SymbolTable,
148        instructions: &'a Vec<SymbolicInstructionInfo<'a>>,
149    ) -> Result<Self, DebuggerError> {
150        let device_properties = Debugger::construct_property_map(properties, &symbol_table)?;
151        let output = Vec::new();
152        Ok(Debugger { device_properties, symbol_table, instructions, output })
153    }
154
155    /// Constructs a map of the device's properties. The keys are of type Symbol, and only keys
156    /// which appear in the bind rules's symbol table are included. Any other keys in the device
157    /// specification can be safely ignored, since the bind rules won't check their values. The
158    /// values are a struct containing both a symbol and an identifier, to allow the debugger to
159    /// output both the identifier and the literal value it corresponds to when printing values.
160    /// Both these fields are optional. If the symbol is None, then the device value is an
161    /// identifier not defined in the symbol table. If the identifier is None, then the device
162    /// value is a literal.
163    fn construct_property_map(
164        properties: &[Property],
165        symbol_table: &SymbolTable,
166    ) -> Result<DevicePropertyMap, DebuggerError> {
167        let mut property_map = HashMap::new();
168        let mut keys_seen = HashSet::new();
169
170        for Property { key, value } in properties {
171            // Check for duplicate keys in the device specification. This is done using a separate
172            // set since the property_map only contains keys which are present in the symbol table.
173            if keys_seen.contains(key) {
174                return Err(DebuggerError::DuplicateKey(key.clone()));
175            }
176            keys_seen.insert(key.clone());
177
178            if let Some(key_symbol) = symbol_table.get(key) {
179                let device_value = match value {
180                    Value::NumericLiteral(n) => {
181                        DeviceValue { symbol: Some(Symbol::NumberValue(*n)), identifier: None }
182                    }
183                    Value::StringLiteral(s) => DeviceValue {
184                        symbol: Some(Symbol::StringValue(s.to_string())),
185                        identifier: None,
186                    },
187                    Value::BoolLiteral(b) => {
188                        DeviceValue { symbol: Some(Symbol::BoolValue(*b)), identifier: None }
189                    }
190                    Value::Identifier(identifier) => match symbol_table.get(identifier) {
191                        Some(symbol) => DeviceValue {
192                            symbol: Some(symbol.clone()),
193                            identifier: Some(identifier.clone()),
194                        },
195                        None => DeviceValue { symbol: None, identifier: Some(identifier.clone()) },
196                    },
197                };
198
199                property_map.insert(key_symbol.clone(), device_value);
200            }
201        }
202
203        Ok(property_map)
204    }
205
206    fn evaluate_bind_rules(&mut self) -> Result<bool, DebuggerError> {
207        let mut instructions = self.instructions.iter();
208
209        while let Some(mut instruction) = instructions.next() {
210            let mut jump_label = None;
211
212            match &instruction.instruction {
213                SymbolicInstruction::AbortIfEqual { lhs, rhs } => {
214                    let aborts = self.device_property_matches(lhs, rhs);
215                    self.output_abort_if(&instruction.location, aborts)?;
216                    if aborts {
217                        return Ok(false);
218                    }
219                }
220                SymbolicInstruction::AbortIfNotEqual { lhs, rhs } => {
221                    let aborts = !self.device_property_matches(lhs, rhs);
222                    self.output_abort_if(&instruction.location, aborts)?;
223                    if aborts {
224                        return Ok(false);
225                    }
226                }
227                SymbolicInstruction::Label(_label) => (),
228                SymbolicInstruction::UnconditionalJump { label } => {
229                    jump_label = Some(label);
230                }
231                SymbolicInstruction::JumpIfEqual { lhs, rhs, label } => {
232                    let jump_succeeds = self.device_property_matches(lhs, rhs);
233                    self.output_jump_if_equal(&instruction.location, rhs, jump_succeeds)?;
234                    if jump_succeeds {
235                        jump_label = Some(label);
236                    }
237                }
238                SymbolicInstruction::JumpIfNotEqual { lhs, rhs, label } => {
239                    let jump_succeeds = !self.device_property_matches(lhs, rhs);
240                    self.output_jump_if_not_equal(&instruction.location, jump_succeeds)?;
241                    if jump_succeeds {
242                        jump_label = Some(label);
243                    }
244                }
245                SymbolicInstruction::UnconditionalAbort => {
246                    self.output_unconditional_abort(&instruction.location)?;
247                    return Ok(false);
248                }
249                SymbolicInstruction::UnconditionalBind => return Ok(true),
250            }
251
252            if let Some(label) = jump_label {
253                while instruction.instruction != SymbolicInstruction::Label(*label) {
254                    instruction = instructions.next().ok_or(DebuggerError::MissingLabel)?;
255                }
256            }
257        }
258
259        Err(DebuggerError::NoOutcome)
260    }
261
262    fn device_property_matches(&self, lhs: &Symbol, rhs: &Symbol) -> bool {
263        if let Some(DeviceValue { symbol: Some(value_symbol), identifier: _ }) =
264            self.device_properties.get(lhs)
265        {
266            value_symbol == rhs
267        } else {
268            false
269        }
270    }
271
272    fn output_abort_if(
273        &mut self,
274        location: &'a Option<AstLocation>,
275        aborts: bool,
276    ) -> Result<(), DebuggerError> {
277        if let Some(AstLocation::ConditionStatement(statement)) = location {
278            self.output.push(DebuggerOutput::ConditionStatement { statement, success: !aborts });
279            Ok(())
280        } else {
281            Err(DebuggerError::IncorrectAstLocation)
282        }
283    }
284
285    fn output_jump_if_equal(
286        &mut self,
287        location: &'a Option<AstLocation>,
288        rhs: &'a Symbol,
289        jump_succeeds: bool,
290    ) -> Result<(), DebuggerError> {
291        match location {
292            Some(AstLocation::AcceptStatementValue { identifier, value, span }) => {
293                if jump_succeeds {
294                    self.output.push(DebuggerOutput::AcceptStatementSuccess {
295                        identifier,
296                        value,
297                        value_symbol: rhs,
298                        span,
299                    });
300                }
301                Ok(())
302            }
303            Some(AstLocation::IfCondition(condition)) => {
304                self.output
305                    .push(DebuggerOutput::IfCondition { condition, success: !jump_succeeds });
306                Ok(())
307            }
308            _ => Err(DebuggerError::IncorrectAstLocation),
309        }
310    }
311
312    fn output_jump_if_not_equal(
313        &mut self,
314        location: &'a Option<AstLocation>,
315        jump_succeeds: bool,
316    ) -> Result<(), DebuggerError> {
317        if let Some(AstLocation::IfCondition(condition)) = location {
318            self.output.push(DebuggerOutput::IfCondition { condition, success: !jump_succeeds });
319            Ok(())
320        } else {
321            Err(DebuggerError::IncorrectAstLocation)
322        }
323    }
324
325    fn output_unconditional_abort(
326        &mut self,
327        location: &'a Option<AstLocation>,
328    ) -> Result<(), DebuggerError> {
329        match location {
330            Some(AstLocation::FalseStatement(statement)) => {
331                self.output.push(DebuggerOutput::FalseStatement { statement });
332                Ok(())
333            }
334            Some(AstLocation::AcceptStatementFailure { identifier, span, symbol: _ }) => {
335                self.output.push(DebuggerOutput::AcceptStatementFailure { identifier, span });
336                Ok(())
337            }
338            _ => Err(DebuggerError::IncorrectAstLocation),
339        }
340    }
341
342    fn log_output(&self) -> Result<(), DebuggerError> {
343        for output in &self.output {
344            match output {
345                DebuggerOutput::ConditionStatement { statement, success } => {
346                    self.log_condition_statement(statement, *success)?;
347                }
348                DebuggerOutput::FalseStatement { statement } => self.log_abort_statement(statement),
349                DebuggerOutput::AcceptStatementSuccess {
350                    identifier,
351                    value,
352                    value_symbol,
353                    span,
354                } => self.log_accept_statement_success(identifier, value, value_symbol, span)?,
355                DebuggerOutput::AcceptStatementFailure { identifier, span } => {
356                    self.log_accept_statement_failure(identifier, span)?;
357                }
358                DebuggerOutput::IfCondition { condition, success } => {
359                    self.log_if_condition(condition, *success)?;
360                }
361            }
362        }
363        Ok(())
364    }
365
366    fn log_condition_statement(
367        &self,
368        statement: &Statement,
369        success: bool,
370    ) -> Result<(), DebuggerError> {
371        if let Statement::ConditionStatement { span, condition: Condition { lhs, op, .. } } =
372            statement
373        {
374            let outcome_string = if success { "succeeded" } else { "failed" };
375            println!(
376                "Line {}: Condition statement {}: {}",
377                span.line, outcome_string, span.fragment
378            );
379
380            if condition_needs_actual_value(success, op) {
381                println!("\t{}", self.actual_value_string(lhs)?)
382            }
383
384            Ok(())
385        } else {
386            Err(DebuggerError::InvalidAstLocation)
387        }
388    }
389
390    fn log_abort_statement(&self, statement: &Statement) {
391        if let Statement::False { span } = statement {
392            println!("Line {}: Abort statement reached.", span.line);
393        }
394    }
395
396    fn log_accept_statement_success(
397        &self,
398        identifier: &CompoundIdentifier,
399        value: &Value,
400        value_symbol: &Symbol,
401        span: &Span,
402    ) -> Result<(), DebuggerError> {
403        // Get the value identifier from the accept statement (or None for a literal value).
404        let value_identifier_prog =
405            if let Value::Identifier(identifier) = value { Some(identifier) } else { None };
406
407        // Get the value identifier from the device specification (or None for a literal value).
408        let key_symbol = self
409            .symbol_table
410            .get(identifier)
411            .ok_or_else(|| DebuggerError::UnknownKey(identifier.clone()))?;
412        let DeviceValue { symbol: _, identifier: value_identifier_device } = self
413            .device_properties
414            .get(key_symbol)
415            .expect("Accept statement succeeded so device must have this key.");
416
417        let value_literal = value_symbol_string(value_symbol)?;
418
419        let value_string = match (value_identifier_prog, value_identifier_device) {
420            (Some(identifier_prog), Some(identifier_value)) => {
421                if identifier_prog == identifier_value {
422                    format!("`{}` [{}]", identifier_prog, value_literal)
423                } else {
424                    format!("`{}` (`{}`) [{}]", identifier_prog, identifier_value, value_literal)
425                }
426            }
427            (Some(identifier_prog), None) => format!("`{}` [{}]", identifier_prog, value_literal),
428            (None, Some(identifier_value)) => format!("`{}` [{}]", identifier_value, value_literal),
429            (None, None) => format!("{}", value_literal),
430        };
431
432        println!(
433            "Line {}: Accept statement succeeded.\n\tValue of `{}` was {}.",
434            span.line, identifier, value_string
435        );
436
437        Ok(())
438    }
439
440    fn log_accept_statement_failure(
441        &self,
442        identifier: &CompoundIdentifier,
443        span: &Span,
444    ) -> Result<(), DebuggerError> {
445        println!(
446            "Line {}: Accept statement failed.\n\t{}",
447            span.line,
448            self.actual_value_string(identifier)?
449        );
450
451        Ok(())
452    }
453
454    fn log_if_condition(&self, condition: &Condition, success: bool) -> Result<(), DebuggerError> {
455        let Condition { span, lhs, op, rhs: _ } = condition;
456
457        let outcome_string = if success { "succeeded" } else { "failed" };
458        println!(
459            "Line {}: If statement condition {}: {}",
460            span.line, outcome_string, span.fragment
461        );
462
463        if condition_needs_actual_value(success, op) {
464            println!("\t{}", self.actual_value_string(lhs)?)
465        }
466
467        Ok(())
468    }
469
470    fn actual_value_string(
471        &self,
472        key_identifier: &CompoundIdentifier,
473    ) -> Result<String, DebuggerError> {
474        let key_symbol = self
475            .symbol_table
476            .get(key_identifier)
477            .ok_or_else(|| DebuggerError::UnknownKey(key_identifier.clone()))?;
478        let DeviceValue { symbol: value_symbol, identifier: value_identifier } = self
479            .device_properties
480            .get(key_symbol)
481            .unwrap_or(&DeviceValue { symbol: None, identifier: None });
482
483        Ok(match (value_symbol, value_identifier) {
484            (Some(symbol), Some(identifier)) => format!(
485                "Actual value of `{}` was `{}` [{}].",
486                key_identifier,
487                identifier,
488                value_symbol_string(symbol)?
489            ),
490            (Some(symbol), None) => format!(
491                "Actual value of `{}` was literal {}.",
492                key_identifier,
493                value_symbol_string(symbol)?
494            ),
495            (None, Some(identifier)) => {
496                format!("Actual value of `{}` was `{}`.", key_identifier, identifier)
497            }
498            (None, None) => format!("Device had no value for `{}`.", key_identifier),
499        })
500    }
501}
502
503fn condition_needs_actual_value(success: bool, op: &ConditionOp) -> bool {
504    match op {
505        ConditionOp::Equals => !success,
506        ConditionOp::NotEquals => success,
507    }
508}
509
510fn value_symbol_string(symbol: &Symbol) -> Result<String, DebuggerError> {
511    match symbol {
512        Symbol::DeprecatedKey(..) => Err(DebuggerError::InvalidValueSymbol(symbol.clone())),
513        Symbol::Key(..) => Err(DebuggerError::InvalidValueSymbol(symbol.clone())),
514        Symbol::NumberValue(n) => Ok(format!("0x{:x}", n)),
515        Symbol::StringValue(s) => Ok(format!("\"{}\"", s)),
516        Symbol::BoolValue(b) => Ok(b.to_string()),
517        Symbol::EnumValue(s) => Ok(format!("\"{}\"", s)),
518    }
519}
520
521#[cfg(test)]
522mod test {
523    use super::*;
524    use crate::make_identifier;
525    use crate::parser::bind_library;
526
527    #[test]
528    fn duplicate_key() {
529        let symbol_table = HashMap::new();
530        let properties = vec![
531            Property { key: make_identifier!("abc"), value: Value::NumericLiteral(42) },
532            Property { key: make_identifier!("abc"), value: Value::NumericLiteral(5) },
533        ];
534        assert_eq!(
535            Debugger::construct_property_map(&properties, &symbol_table),
536            Err(DebuggerError::DuplicateKey(make_identifier!("abc")))
537        );
538    }
539
540    #[test]
541    fn condition_equals() {
542        let statement = Statement::ConditionStatement {
543            span: Span::new(),
544            condition: Condition {
545                span: Span::new(),
546                lhs: make_identifier!("abc"),
547                op: ConditionOp::Equals,
548                rhs: Value::NumericLiteral(42),
549            },
550        };
551        let statements = vec![statement.clone()];
552        let mut symbol_table = HashMap::new();
553        symbol_table.insert(
554            make_identifier!("abc"),
555            Symbol::Key("abc".to_string(), bind_library::ValueType::Number),
556        );
557
558        let instructions = compiler::compile_statements(statements, &symbol_table, false).unwrap();
559
560        // Binds when the device has the correct property.
561        let properties =
562            vec![Property { key: make_identifier!("abc"), value: Value::NumericLiteral(42) }];
563        let mut debugger = Debugger::new(&properties, &symbol_table, &instructions).unwrap();
564        assert!(debugger.evaluate_bind_rules().unwrap());
565        assert_eq!(
566            debugger.output,
567            vec![DebuggerOutput::ConditionStatement { statement: &statement, success: true }]
568        );
569
570        // Binds when other properties are present as well.
571        let properties = vec![
572            Property { key: make_identifier!("abc"), value: Value::NumericLiteral(42) },
573            Property { key: make_identifier!("xyz"), value: Value::BoolLiteral(true) },
574        ];
575        let mut debugger = Debugger::new(&properties, &symbol_table, &instructions).unwrap();
576        assert!(debugger.evaluate_bind_rules().unwrap());
577        assert_eq!(
578            debugger.output,
579            vec![DebuggerOutput::ConditionStatement { statement: &statement, success: true }]
580        );
581
582        // Doesn't bind when the device has the wrong value for the property.
583        let properties =
584            vec![Property { key: make_identifier!("abc"), value: Value::NumericLiteral(5) }];
585        let mut debugger = Debugger::new(&properties, &symbol_table, &instructions).unwrap();
586        assert!(!debugger.evaluate_bind_rules().unwrap());
587        assert_eq!(
588            debugger.output,
589            vec![DebuggerOutput::ConditionStatement { statement: &statement, success: false }]
590        );
591
592        // Doesn't bind when the property is not present in the device.
593        let properties = Vec::new();
594        let mut debugger = Debugger::new(&properties, &symbol_table, &instructions).unwrap();
595        assert!(!debugger.evaluate_bind_rules().unwrap());
596        assert_eq!(
597            debugger.output,
598            vec![DebuggerOutput::ConditionStatement { statement: &statement, success: false }]
599        );
600    }
601
602    #[test]
603    fn condition_not_equals() {
604        let statement = Statement::ConditionStatement {
605            span: Span::new(),
606            condition: Condition {
607                span: Span::new(),
608                lhs: make_identifier!("abc"),
609                op: ConditionOp::NotEquals,
610                rhs: Value::NumericLiteral(42),
611            },
612        };
613        let statements = vec![statement.clone()];
614        let mut symbol_table = HashMap::new();
615        symbol_table.insert(
616            make_identifier!("abc"),
617            Symbol::Key("abc".to_string(), bind_library::ValueType::Number),
618        );
619        let instructions = compiler::compile_statements(statements, &symbol_table, false).unwrap();
620
621        // Binds when the device has a different value for the property.
622        let properties =
623            vec![Property { key: make_identifier!("abc"), value: Value::NumericLiteral(5) }];
624        let mut debugger = Debugger::new(&properties, &symbol_table, &instructions).unwrap();
625        assert!(debugger.evaluate_bind_rules().unwrap());
626        assert_eq!(
627            debugger.output,
628            vec![DebuggerOutput::ConditionStatement { statement: &statement, success: true }]
629        );
630
631        // Binds when the property is not present in the device.
632        let properties = Vec::new();
633        let mut debugger = Debugger::new(&properties, &symbol_table, &instructions).unwrap();
634        assert!(debugger.evaluate_bind_rules().unwrap());
635        assert_eq!(
636            debugger.output,
637            vec![DebuggerOutput::ConditionStatement { statement: &statement, success: true }]
638        );
639
640        // Doesn't bind when the device has the property in the condition statement.
641        let properties =
642            vec![Property { key: make_identifier!("abc"), value: Value::NumericLiteral(42) }];
643        let mut debugger = Debugger::new(&properties, &symbol_table, &instructions).unwrap();
644        assert!(!debugger.evaluate_bind_rules().unwrap());
645        assert_eq!(
646            debugger.output,
647            vec![DebuggerOutput::ConditionStatement { statement: &statement, success: false }]
648        );
649    }
650
651    #[test]
652    fn accept() {
653        let statements = vec![Statement::Accept {
654            span: Span::new(),
655            identifier: make_identifier!("abc"),
656            values: vec![Value::NumericLiteral(42), Value::NumericLiteral(314)],
657        }];
658        let mut symbol_table = HashMap::new();
659        symbol_table.insert(
660            make_identifier!("abc"),
661            Symbol::Key("abc".to_string(), bind_library::ValueType::Number),
662        );
663
664        let instructions = compiler::compile_statements(statements, &symbol_table, false).unwrap();
665
666        // Binds when the device has one of the accepted values for the property.
667        let properties =
668            vec![Property { key: make_identifier!("abc"), value: Value::NumericLiteral(42) }];
669        let mut debugger = Debugger::new(&properties, &symbol_table, &instructions).unwrap();
670        assert!(debugger.evaluate_bind_rules().unwrap());
671        assert_eq!(
672            debugger.output,
673            vec![DebuggerOutput::AcceptStatementSuccess {
674                identifier: &make_identifier!("abc"),
675                value: &Value::NumericLiteral(42),
676                value_symbol: &Symbol::NumberValue(42),
677                span: &Span::new(),
678            }]
679        );
680
681        // Doesn't bind when the device has a different value for the property.
682        let properties =
683            vec![Property { key: make_identifier!("abc"), value: Value::NumericLiteral(5) }];
684        let mut debugger = Debugger::new(&properties, &symbol_table, &instructions).unwrap();
685        assert!(!debugger.evaluate_bind_rules().unwrap());
686        assert_eq!(
687            debugger.output,
688            vec![DebuggerOutput::AcceptStatementFailure {
689                identifier: &make_identifier!("abc"),
690                span: &Span::new(),
691            }]
692        );
693
694        // Doesn't bind when the device is missing the property.
695        let properties = Vec::new();
696        let mut debugger = Debugger::new(&properties, &symbol_table, &instructions).unwrap();
697        assert!(!debugger.evaluate_bind_rules().unwrap());
698        assert_eq!(
699            debugger.output,
700            vec![DebuggerOutput::AcceptStatementFailure {
701                identifier: &make_identifier!("abc"),
702                span: &Span::new(),
703            }]
704        );
705    }
706
707    #[test]
708    fn if_else() {
709        /*
710        if abc == 1 {
711            xyz == 1;
712        } else if abc == 2{
713            xyz == 2;
714        } else {
715            xyz == 3;
716        }
717        */
718
719        let condition1 = Condition {
720            span: Span::new(),
721            lhs: make_identifier!("abc"),
722            op: ConditionOp::Equals,
723            rhs: Value::NumericLiteral(1),
724        };
725        let condition2 = Condition {
726            span: Span::new(),
727            lhs: make_identifier!("abc"),
728            op: ConditionOp::Equals,
729            rhs: Value::NumericLiteral(2),
730        };
731        let statement1 = Statement::ConditionStatement {
732            span: Span::new(),
733            condition: Condition {
734                span: Span::new(),
735                lhs: make_identifier!("xyz"),
736                op: ConditionOp::Equals,
737                rhs: Value::NumericLiteral(1),
738            },
739        };
740        let statement2 = Statement::ConditionStatement {
741            span: Span::new(),
742            condition: Condition {
743                span: Span::new(),
744                lhs: make_identifier!("xyz"),
745                op: ConditionOp::Equals,
746                rhs: Value::NumericLiteral(2),
747            },
748        };
749        let statement3 = Statement::ConditionStatement {
750            span: Span::new(),
751            condition: Condition {
752                span: Span::new(),
753                lhs: make_identifier!("xyz"),
754                op: ConditionOp::Equals,
755                rhs: Value::NumericLiteral(3),
756            },
757        };
758
759        let statements = vec![Statement::If {
760            span: Span::new(),
761            blocks: vec![
762                (condition1.clone(), vec![statement1.clone()]),
763                (condition2.clone(), vec![statement2.clone()]),
764            ],
765            else_block: vec![statement3.clone()],
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        symbol_table.insert(
773            make_identifier!("xyz"),
774            Symbol::Key("xyz".to_string(), bind_library::ValueType::Number),
775        );
776
777        let instructions = compiler::compile_statements(statements, &symbol_table, false).unwrap();
778
779        // Binds when the if clause is satisfied.
780        let properties = vec![
781            Property { key: make_identifier!("abc"), value: Value::NumericLiteral(1) },
782            Property { key: make_identifier!("xyz"), value: Value::NumericLiteral(1) },
783        ];
784        let mut debugger = Debugger::new(&properties, &symbol_table, &instructions).unwrap();
785        assert!(debugger.evaluate_bind_rules().unwrap());
786        assert_eq!(
787            debugger.output,
788            vec![
789                DebuggerOutput::IfCondition { condition: &condition1, success: true },
790                DebuggerOutput::ConditionStatement { statement: &statement1, success: true }
791            ]
792        );
793
794        // Binds when the if else clause is satisfied.
795        let properties = vec![
796            Property { key: make_identifier!("abc"), value: Value::NumericLiteral(2) },
797            Property { key: make_identifier!("xyz"), value: Value::NumericLiteral(2) },
798        ];
799        let mut debugger = Debugger::new(&properties, &symbol_table, &instructions).unwrap();
800        assert!(debugger.evaluate_bind_rules().unwrap());
801        assert_eq!(
802            debugger.output,
803            vec![
804                DebuggerOutput::IfCondition { condition: &condition1, success: false },
805                DebuggerOutput::IfCondition { condition: &condition2, success: true },
806                DebuggerOutput::ConditionStatement { statement: &statement2, success: true }
807            ]
808        );
809
810        // Binds when the else clause is satisfied.
811        let properties =
812            vec![Property { key: make_identifier!("xyz"), value: Value::NumericLiteral(3) }];
813        let mut debugger = Debugger::new(&properties, &symbol_table, &instructions).unwrap();
814        assert!(debugger.evaluate_bind_rules().unwrap());
815        assert_eq!(
816            debugger.output,
817            vec![
818                DebuggerOutput::IfCondition { condition: &condition1, success: false },
819                DebuggerOutput::IfCondition { condition: &condition2, success: false },
820                DebuggerOutput::ConditionStatement { statement: &statement3, success: true }
821            ]
822        );
823
824        // Doesn't bind when the device has incorrect values for the properties.
825        let properties = vec![
826            Property { key: make_identifier!("abc"), value: Value::NumericLiteral(42) },
827            Property { key: make_identifier!("xyz"), value: Value::NumericLiteral(42) },
828        ];
829        let mut debugger = Debugger::new(&properties, &symbol_table, &instructions).unwrap();
830        assert!(!debugger.evaluate_bind_rules().unwrap());
831        assert_eq!(
832            debugger.output,
833            vec![
834                DebuggerOutput::IfCondition { condition: &condition1, success: false },
835                DebuggerOutput::IfCondition { condition: &condition2, success: false },
836                DebuggerOutput::ConditionStatement { statement: &statement3, success: false }
837            ]
838        );
839
840        // Doesn't bind when the properties are missing in the device.
841        let properties = Vec::new();
842        let mut debugger = Debugger::new(&properties, &symbol_table, &instructions).unwrap();
843        assert!(!debugger.evaluate_bind_rules().unwrap());
844        assert_eq!(
845            debugger.output,
846            vec![
847                DebuggerOutput::IfCondition { condition: &condition1, success: false },
848                DebuggerOutput::IfCondition { condition: &condition2, success: false },
849                DebuggerOutput::ConditionStatement { statement: &statement3, success: false }
850            ]
851        );
852    }
853
854    #[test]
855    fn abort() {
856        let abort_statement = Statement::False { span: Span::new() };
857        let statements = vec![abort_statement.clone()];
858        let mut symbol_table = HashMap::new();
859        symbol_table.insert(
860            make_identifier!("abc"),
861            Symbol::Key("abc".to_string(), bind_library::ValueType::Number),
862        );
863
864        let instructions = compiler::compile_statements(statements, &symbol_table, false).unwrap();
865
866        // Doesn't bind when abort statement is present.
867        let properties =
868            vec![Property { key: make_identifier!("abc"), value: Value::NumericLiteral(42) }];
869        let mut debugger = Debugger::new(&properties, &symbol_table, &instructions).unwrap();
870        assert!(!debugger.evaluate_bind_rules().unwrap());
871        assert_eq!(
872            debugger.output,
873            vec![DebuggerOutput::FalseStatement { statement: &abort_statement }]
874        );
875    }
876
877    #[test]
878    fn supports_all_value_types() {
879        let statements = vec![
880            Statement::ConditionStatement {
881                span: Span::new(),
882                condition: Condition {
883                    span: Span::new(),
884                    lhs: make_identifier!("a"),
885                    op: ConditionOp::Equals,
886                    rhs: Value::NumericLiteral(42),
887                },
888            },
889            Statement::ConditionStatement {
890                span: Span::new(),
891                condition: Condition {
892                    span: Span::new(),
893                    lhs: make_identifier!("b"),
894                    op: ConditionOp::Equals,
895                    rhs: Value::BoolLiteral(false),
896                },
897            },
898            Statement::ConditionStatement {
899                span: Span::new(),
900                condition: Condition {
901                    span: Span::new(),
902                    lhs: make_identifier!("c"),
903                    op: ConditionOp::Equals,
904                    rhs: Value::StringLiteral("string".to_string()),
905                },
906            },
907            Statement::ConditionStatement {
908                span: Span::new(),
909                condition: Condition {
910                    span: Span::new(),
911                    lhs: make_identifier!("d"),
912                    op: ConditionOp::Equals,
913                    rhs: Value::Identifier(make_identifier!("VALUE")),
914                },
915            },
916        ];
917        let mut symbol_table = HashMap::new();
918        symbol_table.insert(
919            make_identifier!("a"),
920            Symbol::Key("a".to_string(), bind_library::ValueType::Number),
921        );
922        symbol_table.insert(
923            make_identifier!("b"),
924            Symbol::Key("b".to_string(), bind_library::ValueType::Number),
925        );
926        symbol_table.insert(
927            make_identifier!("c"),
928            Symbol::Key("c".to_string(), bind_library::ValueType::Number),
929        );
930        symbol_table.insert(
931            make_identifier!("d"),
932            Symbol::Key("d".to_string(), bind_library::ValueType::Number),
933        );
934        symbol_table.insert(
935            make_identifier!("VALUE"),
936            Symbol::Key("VALUE".to_string(), bind_library::ValueType::Number),
937        );
938
939        let instructions = compiler::compile_statements(statements, &symbol_table, false).unwrap();
940
941        // Binds when other properties are present as well.
942        let properties = vec![
943            Property { key: make_identifier!("a"), value: Value::NumericLiteral(42) },
944            Property { key: make_identifier!("b"), value: Value::BoolLiteral(false) },
945            Property {
946                key: make_identifier!("c"),
947                value: Value::StringLiteral("string".to_string()),
948            },
949            Property {
950                key: make_identifier!("d"),
951                value: Value::Identifier(make_identifier!("VALUE")),
952            },
953        ];
954        let mut debugger = Debugger::new(&properties, &symbol_table, &instructions).unwrap();
955        assert!(debugger.evaluate_bind_rules().unwrap());
956    }
957
958    #[test]
959    fn full_rules() {
960        /*
961        if abc == 42 {
962            abort;
963        } else {
964            accept xyz {1, 2};
965            pqr != true;
966        }
967        */
968
969        let condition = Condition {
970            span: Span::new(),
971            lhs: make_identifier!("abc"),
972            op: ConditionOp::Equals,
973            rhs: Value::NumericLiteral(42),
974        };
975        let abort_statement = Statement::False { span: Span::new() };
976        let condition_statement = Statement::ConditionStatement {
977            span: Span::new(),
978            condition: Condition {
979                span: Span::new(),
980                lhs: make_identifier!("pqr"),
981                op: ConditionOp::NotEquals,
982                rhs: Value::BoolLiteral(true),
983            },
984        };
985
986        let statements = vec![Statement::If {
987            span: Span::new(),
988            blocks: vec![(condition.clone(), vec![abort_statement.clone()])],
989            else_block: vec![
990                Statement::Accept {
991                    span: Span::new(),
992                    identifier: make_identifier!("xyz"),
993                    values: vec![Value::NumericLiteral(1), Value::NumericLiteral(2)],
994                },
995                condition_statement.clone(),
996            ],
997        }];
998        let mut symbol_table = HashMap::new();
999        symbol_table.insert(
1000            make_identifier!("abc"),
1001            Symbol::Key("abc".to_string(), bind_library::ValueType::Number),
1002        );
1003        symbol_table.insert(
1004            make_identifier!("xyz"),
1005            Symbol::Key("xyz".to_string(), bind_library::ValueType::Number),
1006        );
1007        symbol_table.insert(
1008            make_identifier!("pqr"),
1009            Symbol::Key("pqr".to_string(), bind_library::ValueType::Number),
1010        );
1011        let instructions = compiler::compile_statements(statements, &symbol_table, false).unwrap();
1012
1013        // Aborts because if condition is true.
1014        let properties =
1015            vec![Property { key: make_identifier!("abc"), value: Value::NumericLiteral(42) }];
1016        let mut debugger = Debugger::new(&properties, &symbol_table, &instructions).unwrap();
1017        assert!(!debugger.evaluate_bind_rules().unwrap());
1018        assert_eq!(
1019            debugger.output,
1020            vec![
1021                DebuggerOutput::IfCondition { condition: &condition, success: true },
1022                DebuggerOutput::FalseStatement { statement: &abort_statement }
1023            ]
1024        );
1025
1026        // Binds because all statements inside else block are satisfied.
1027        let properties = vec![
1028            Property { key: make_identifier!("abc"), value: Value::NumericLiteral(43) },
1029            Property { key: make_identifier!("xyz"), value: Value::NumericLiteral(1) },
1030        ];
1031        let mut debugger = Debugger::new(&properties, &symbol_table, &instructions).unwrap();
1032        assert!(debugger.evaluate_bind_rules().unwrap());
1033        assert_eq!(
1034            debugger.output,
1035            vec![
1036                DebuggerOutput::IfCondition { condition: &condition, success: false },
1037                DebuggerOutput::AcceptStatementSuccess {
1038                    identifier: &make_identifier!("xyz"),
1039                    value: &Value::NumericLiteral(1),
1040                    value_symbol: &Symbol::NumberValue(1),
1041                    span: &Span::new(),
1042                },
1043                DebuggerOutput::ConditionStatement {
1044                    statement: &condition_statement,
1045                    success: true
1046                }
1047            ]
1048        );
1049
1050        // Doesn't bind because accept statement is not satisfied.
1051        let properties = vec![
1052            Property { key: make_identifier!("abc"), value: Value::NumericLiteral(43) },
1053            Property { key: make_identifier!("xyz"), value: Value::NumericLiteral(3) },
1054        ];
1055        let mut debugger = Debugger::new(&properties, &symbol_table, &instructions).unwrap();
1056        assert!(!debugger.evaluate_bind_rules().unwrap());
1057        assert_eq!(
1058            debugger.output,
1059            vec![
1060                DebuggerOutput::IfCondition { condition: &condition, success: false },
1061                DebuggerOutput::AcceptStatementFailure {
1062                    identifier: &make_identifier!("xyz"),
1063                    span: &Span::new(),
1064                },
1065            ]
1066        );
1067
1068        // Doesn't bind because condition statement is not satisfied.
1069        let properties = vec![
1070            Property { key: make_identifier!("abc"), value: Value::NumericLiteral(43) },
1071            Property { key: make_identifier!("xyz"), value: Value::NumericLiteral(1) },
1072            Property { key: make_identifier!("pqr"), value: Value::BoolLiteral(true) },
1073        ];
1074        let mut debugger = Debugger::new(&properties, &symbol_table, &instructions).unwrap();
1075        assert!(!debugger.evaluate_bind_rules().unwrap());
1076        assert_eq!(
1077            debugger.output,
1078            vec![
1079                DebuggerOutput::IfCondition { condition: &condition, success: false },
1080                DebuggerOutput::AcceptStatementSuccess {
1081                    identifier: &make_identifier!("xyz"),
1082                    value: &Value::NumericLiteral(1),
1083                    value_symbol: &Symbol::NumberValue(1),
1084                    span: &Span::new(),
1085                },
1086                DebuggerOutput::ConditionStatement {
1087                    statement: &condition_statement,
1088                    success: false
1089                }
1090            ]
1091        );
1092    }
1093}