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::{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}
37
38impl fmt::Display for CompilerError {
39    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40        write!(f, "{}", UserError::from(self.clone()))
41    }
42}
43
44#[derive(Debug, Error, Clone, PartialEq)]
45pub enum BindRulesDecodeError {
46    InvalidBinaryLength,
47}
48
49impl fmt::Display for BindRulesDecodeError {
50    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51        write!(f, "{}", UserError::from(self.clone()))
52    }
53}
54
55#[derive(Debug, PartialEq)]
56pub enum CompiledBindRules<'a> {
57    Bind(BindRules<'a>),
58    CompositeBind(CompositeBindRules<'a>),
59}
60
61impl<'a> CompiledBindRules<'a> {
62    pub fn encode_to_bytecode(self) -> Result<Vec<u8>, BindRulesEncodeError> {
63        match self {
64            CompiledBindRules::Bind(bind_rules) => {
65                if bind_rules.use_new_bytecode {
66                    return encode_to_bytecode_v2(bind_rules);
67                }
68
69                encode_to_bytecode_v1(bind_rules)
70            }
71            CompiledBindRules::CompositeBind(composite_bind) => {
72                encode_composite_to_bytecode(composite_bind)
73            }
74        }
75    }
76
77    pub fn empty_bind_rules(
78        use_new_bytecode: bool,
79        disable_autobind: bool,
80        enable_debug: bool,
81    ) -> CompiledBindRules<'a> {
82        let mut instructions = vec![];
83
84        if disable_autobind {
85            instructions.push(SymbolicInstructionInfo::disable_autobind());
86        }
87
88        if !use_new_bytecode {
89            instructions.push(SymbolicInstructionInfo {
90                location: None,
91                instruction: SymbolicInstruction::UnconditionalBind,
92            });
93        }
94
95        CompiledBindRules::Bind(BindRules {
96            instructions: instructions,
97            symbol_table: HashMap::new(),
98            use_new_bytecode: use_new_bytecode,
99            enable_debug: enable_debug,
100        })
101    }
102}
103
104// TODO(https://fxbug.dev/42054389): Use enable debug flag.
105#[derive(Debug, PartialEq)]
106pub struct BindRules<'a> {
107    pub symbol_table: SymbolTable,
108    pub instructions: Vec<SymbolicInstructionInfo<'a>>,
109    pub use_new_bytecode: bool,
110    pub enable_debug: bool,
111}
112
113#[derive(Clone, Debug, PartialEq)]
114pub enum SymbolicInstruction {
115    AbortIfEqual { lhs: Symbol, rhs: Symbol },
116    AbortIfNotEqual { lhs: Symbol, rhs: Symbol },
117    Label(u32),
118    UnconditionalJump { label: u32 },
119    JumpIfEqual { lhs: Symbol, rhs: Symbol, label: u32 },
120    JumpIfNotEqual { lhs: Symbol, rhs: Symbol, label: u32 },
121    UnconditionalAbort,
122    UnconditionalBind,
123}
124
125impl SymbolicInstruction {
126    pub fn to_instruction(self) -> instruction::Instruction {
127        match self {
128            SymbolicInstruction::AbortIfEqual { lhs, rhs } => {
129                instruction::Instruction::Abort(instruction::Condition::Equal(lhs, rhs))
130            }
131            SymbolicInstruction::AbortIfNotEqual { lhs, rhs } => {
132                instruction::Instruction::Abort(instruction::Condition::NotEqual(lhs, rhs))
133            }
134            SymbolicInstruction::Label(label_id) => instruction::Instruction::Label(label_id),
135            SymbolicInstruction::UnconditionalJump { label } => {
136                instruction::Instruction::Goto(instruction::Condition::Always, label)
137            }
138            SymbolicInstruction::JumpIfEqual { lhs, rhs, label } => {
139                instruction::Instruction::Goto(instruction::Condition::Equal(lhs, rhs), label)
140            }
141            SymbolicInstruction::JumpIfNotEqual { lhs, rhs, label } => {
142                instruction::Instruction::Goto(instruction::Condition::NotEqual(lhs, rhs), label)
143            }
144            SymbolicInstruction::UnconditionalAbort => {
145                instruction::Instruction::Abort(instruction::Condition::Always)
146            }
147            SymbolicInstruction::UnconditionalBind => {
148                instruction::Instruction::Match(instruction::Condition::Always)
149            }
150        }
151    }
152}
153
154#[derive(Clone, Debug, PartialEq)]
155pub struct SymbolicInstructionInfo<'a> {
156    pub location: Option<AstLocation<'a>>,
157    pub instruction: SymbolicInstruction,
158}
159
160impl<'a> SymbolicInstructionInfo<'a> {
161    pub fn to_instruction(self) -> instruction::InstructionInfo {
162        instruction::InstructionInfo {
163            instruction: self.instruction.to_instruction(),
164            debug: match self.location {
165                Some(location) => location.to_instruction_debug(),
166                None => instruction::InstructionDebug::none(),
167            },
168        }
169    }
170
171    pub fn disable_autobind() -> Self {
172        SymbolicInstructionInfo {
173            location: None,
174            instruction: SymbolicInstruction::AbortIfNotEqual {
175                lhs: Symbol::DeprecatedKey(BIND_AUTOBIND),
176                rhs: Symbol::NumberValue(0),
177            },
178        }
179    }
180}
181
182#[derive(Debug, PartialEq)]
183pub struct CompositeNode<'a> {
184    pub name: String,
185    pub instructions: Vec<SymbolicInstructionInfo<'a>>,
186}
187
188#[derive(Debug, PartialEq)]
189pub struct CompositeBindRules<'a> {
190    pub device_name: String,
191    pub symbol_table: SymbolTable,
192    pub primary_node: CompositeNode<'a>,
193    pub additional_nodes: Vec<CompositeNode<'a>>,
194    pub optional_nodes: Vec<CompositeNode<'a>>,
195    pub enable_debug: bool,
196}
197
198pub fn compile<'a>(
199    rules_str: &'a str,
200    libraries: &[String],
201    lint: bool,
202    disable_autobind: bool,
203    use_new_bytecode: bool,
204    enable_debug: bool,
205) -> Result<CompiledBindRules<'a>, CompilerError> {
206    if bind_composite::Ast::try_from(rules_str).is_ok() {
207        return Ok(CompiledBindRules::CompositeBind(compile_bind_composite(
208            rules_str,
209            libraries,
210            lint,
211            use_new_bytecode,
212            enable_debug,
213        )?));
214    }
215
216    Ok(CompiledBindRules::Bind(compile_bind(
217        rules_str,
218        libraries,
219        lint,
220        disable_autobind,
221        use_new_bytecode,
222        enable_debug,
223    )?))
224}
225
226pub fn compile_bind<'a>(
227    rules_str: &'a str,
228    libraries: &[String],
229    lint: bool,
230    disable_autobind: bool,
231    use_new_bytecode: bool,
232    enable_debug: bool,
233) -> Result<BindRules<'a>, CompilerError> {
234    let ast = bind_rules::Ast::try_from(rules_str).map_err(CompilerError::BindParserError)?;
235    let symbol_table = get_symbol_table_from_libraries(&ast.using, libraries, lint)?;
236
237    let mut instructions = compile_statements(ast.statements, &symbol_table, use_new_bytecode)?;
238    if disable_autobind {
239        instructions.insert(0, SymbolicInstructionInfo::disable_autobind());
240    }
241
242    Ok(BindRules {
243        symbol_table: symbol_table,
244        instructions: instructions,
245        use_new_bytecode: use_new_bytecode,
246        enable_debug: enable_debug,
247    })
248}
249
250pub fn compile_bind_composite<'a>(
251    rules_str: &'a str,
252    libraries: &[String],
253    lint: bool,
254    use_new_bytecode: bool,
255    enable_debug: bool,
256) -> Result<CompositeBindRules<'a>, CompilerError> {
257    let ast = bind_composite::Ast::try_from(rules_str).map_err(CompilerError::BindParserError)?;
258    let symbol_table = get_symbol_table_from_libraries(&ast.using, libraries, lint)?;
259    let primary_node = CompositeNode {
260        name: ast.primary_node.name,
261        instructions: compile_statements(
262            ast.primary_node.statements,
263            &symbol_table,
264            use_new_bytecode,
265        )?,
266    };
267    let additional_nodes = ast
268        .additional_nodes
269        .into_iter()
270        .map(|node| {
271            let name = node.name;
272            compile_statements(node.statements, &symbol_table, use_new_bytecode)
273                .map(|inst| CompositeNode { name: name, instructions: inst })
274        })
275        .collect::<Result<Vec<CompositeNode<'_>>, CompilerError>>()?;
276
277    let optional_nodes = ast
278        .optional_nodes
279        .into_iter()
280        .map(|node| {
281            let name = node.name;
282            compile_statements(node.statements, &symbol_table, use_new_bytecode)
283                .map(|inst| CompositeNode { name: name, instructions: inst })
284        })
285        .collect::<Result<Vec<CompositeNode<'_>>, CompilerError>>()?;
286
287    Ok(CompositeBindRules {
288        device_name: ast.name.to_string(),
289        symbol_table: symbol_table,
290        primary_node: primary_node,
291        additional_nodes: additional_nodes,
292        optional_nodes: optional_nodes,
293        enable_debug: enable_debug,
294    })
295}
296
297pub fn compile_statements<'a, 'b>(
298    statements: Vec<Statement<'a>>,
299    symbol_table: &'b SymbolTable,
300    use_new_bytecode: bool,
301) -> Result<Vec<SymbolicInstructionInfo<'a>>, CompilerError> {
302    let mut compiler = Compiler::new(symbol_table);
303    compiler.compile_statements(statements, use_new_bytecode)?;
304    Ok(compiler.instructions)
305}
306
307struct Compiler<'a, 'b> {
308    symbol_table: &'b SymbolTable,
309    pub instructions: Vec<SymbolicInstructionInfo<'a>>,
310    next_label_id: u32,
311}
312
313impl<'a, 'b> Compiler<'a, 'b> {
314    fn new(symbol_table: &'b SymbolTable) -> Self {
315        Compiler { symbol_table: symbol_table, instructions: vec![], next_label_id: 0 }
316    }
317
318    fn lookup_identifier(&self, identifier: &CompoundIdentifier) -> Result<Symbol, CompilerError> {
319        let symbol = self
320            .symbol_table
321            .get(identifier)
322            .ok_or_else(|| CompilerError::UnknownKey(identifier.clone()))?;
323        Ok(symbol.clone())
324    }
325
326    fn lookup_value(&self, value: &Value) -> Result<Symbol, CompilerError> {
327        match value {
328            Value::NumericLiteral(n) => Ok(Symbol::NumberValue(*n)),
329            Value::StringLiteral(s) => Ok(Symbol::StringValue(s.to_string())),
330            Value::BoolLiteral(b) => Ok(Symbol::BoolValue(*b)),
331            Value::Identifier(ident) => self
332                .symbol_table
333                .get(ident)
334                .ok_or_else(|| CompilerError::UnknownKey(ident.clone()))
335                .map(|x| x.clone()),
336        }
337    }
338
339    fn compile_statements(
340        &mut self,
341        statements: Vec<Statement<'a>>,
342        use_new_bytecode: bool,
343    ) -> Result<(), CompilerError> {
344        self.compile_block(statements)?;
345
346        // If none of the statements caused an abort, then we should bind the driver.
347        if !use_new_bytecode {
348            self.instructions.push(SymbolicInstructionInfo {
349                location: None,
350                instruction: SymbolicInstruction::UnconditionalBind,
351            });
352        }
353
354        Ok(())
355    }
356
357    fn get_unique_label(&mut self) -> u32 {
358        let label = self.next_label_id;
359        self.next_label_id += 1;
360        label
361    }
362
363    fn compile_block(&mut self, statements: Vec<Statement<'a>>) -> Result<(), CompilerError> {
364        let num_statements = statements.len();
365        let mut iter = statements.into_iter().peekable();
366        while let Some(statement) = iter.next() {
367            match statement {
368                Statement::ConditionStatement { .. } => {
369                    if let Statement::ConditionStatement {
370                        span: _,
371                        condition: Condition { span: _, lhs, op, rhs },
372                    } = &statement
373                    {
374                        let lhs_symbol = self.lookup_identifier(lhs)?;
375                        let rhs_symbol = self.lookup_value(rhs)?;
376                        let instruction = match op {
377                            ConditionOp::Equals => SymbolicInstruction::AbortIfNotEqual {
378                                lhs: lhs_symbol,
379                                rhs: rhs_symbol,
380                            },
381                            ConditionOp::NotEquals => SymbolicInstruction::AbortIfEqual {
382                                lhs: lhs_symbol,
383                                rhs: rhs_symbol,
384                            },
385                        };
386                        self.instructions.push(SymbolicInstructionInfo {
387                            location: Some(AstLocation::ConditionStatement(statement)),
388                            instruction,
389                        });
390                    }
391                }
392                Statement::Accept { span, identifier, values } => {
393                    let lhs_symbol = self.lookup_identifier(&identifier)?;
394                    let label_id = self.get_unique_label();
395                    for value in values {
396                        self.instructions.push(SymbolicInstructionInfo {
397                            location: Some(AstLocation::AcceptStatementValue {
398                                identifier: identifier.clone(),
399                                value: value.clone(),
400                                span: span.clone(),
401                            }),
402                            instruction: SymbolicInstruction::JumpIfEqual {
403                                lhs: lhs_symbol.clone(),
404                                rhs: self.lookup_value(&value)?,
405                                label: label_id,
406                            },
407                        });
408                    }
409                    self.instructions.push(SymbolicInstructionInfo {
410                        location: Some(AstLocation::AcceptStatementFailure {
411                            identifier,
412                            symbol: lhs_symbol,
413                            span,
414                        }),
415                        instruction: SymbolicInstruction::UnconditionalAbort,
416                    });
417                    self.instructions.push(SymbolicInstructionInfo {
418                        location: None,
419                        instruction: SymbolicInstruction::Label(label_id),
420                    });
421                }
422                Statement::If { span: _, blocks, else_block } => {
423                    if !iter.peek().is_none() {
424                        return Err(CompilerError::IfStatementMustBeTerminal);
425                    }
426
427                    let final_label_id = self.get_unique_label();
428
429                    for (condition, block_statements) in blocks {
430                        let Condition { span: _, lhs, op, rhs } = &condition;
431
432                        let lhs_symbol = self.lookup_identifier(lhs)?;
433                        let rhs_symbol = self.lookup_value(rhs)?;
434
435                        // Generate instructions for the condition.
436                        let label_id = self.get_unique_label();
437                        let instruction = match op {
438                            ConditionOp::Equals => SymbolicInstruction::JumpIfNotEqual {
439                                lhs: lhs_symbol,
440                                rhs: rhs_symbol,
441                                label: label_id,
442                            },
443                            ConditionOp::NotEquals => SymbolicInstruction::JumpIfEqual {
444                                lhs: lhs_symbol,
445                                rhs: rhs_symbol,
446                                label: label_id,
447                            },
448                        };
449                        self.instructions.push(SymbolicInstructionInfo {
450                            location: Some(AstLocation::IfCondition(condition)),
451                            instruction,
452                        });
453
454                        // Compile the block itself.
455                        self.compile_block(block_statements)?;
456
457                        // Jump to after the if statement.
458                        self.instructions.push(SymbolicInstructionInfo {
459                            location: None,
460                            instruction: SymbolicInstruction::UnconditionalJump {
461                                label: final_label_id,
462                            },
463                        });
464
465                        // Insert a label to jump to when the condition fails.
466                        self.instructions.push(SymbolicInstructionInfo {
467                            location: None,
468                            instruction: SymbolicInstruction::Label(label_id),
469                        });
470                    }
471
472                    // Compile the else block.
473                    self.compile_block(else_block)?;
474
475                    // Insert a label to jump to at the end of the whole if statement. Note that we
476                    // could just emit an unconditional bind instead of jumping, since we know that
477                    // if statements are terminal, but we do the jump to be consistent with
478                    // condition and accept statements.
479
480                    self.instructions.push(SymbolicInstructionInfo {
481                        location: None,
482                        instruction: SymbolicInstruction::Label(final_label_id),
483                    });
484                }
485                Statement::False { span: _ } => {
486                    if num_statements != 1 {
487                        return Err(CompilerError::FalseStatementMustBeIsolated);
488                    }
489                    self.instructions.push(SymbolicInstructionInfo {
490                        location: Some(AstLocation::FalseStatement(statement)),
491                        instruction: SymbolicInstruction::UnconditionalAbort,
492                    });
493                }
494                Statement::True { .. } => {
495                    // A `true` statement doesn't require an instruction.
496                    if num_statements != 1 {
497                        return Err(CompilerError::TrueStatementMustBeIsolated);
498                    }
499                }
500            }
501        }
502        Ok(())
503    }
504}
505
506#[cfg(test)]
507mod test {
508    use super::*;
509    use crate::make_identifier;
510    use crate::parser::bind_library;
511    use crate::parser::common::{Include, Span};
512
513    #[test]
514    fn condition() {
515        let condition_statement = Statement::ConditionStatement {
516            span: Span::new(),
517            condition: Condition {
518                span: Span::new(),
519                lhs: make_identifier!("abc"),
520                op: ConditionOp::Equals,
521                rhs: Value::NumericLiteral(42),
522            },
523        };
524
525        let rules =
526            bind_rules::Ast { using: vec![], statements: vec![condition_statement.clone()] };
527        let mut symbol_table = HashMap::new();
528        symbol_table.insert(
529            make_identifier!("abc"),
530            Symbol::Key("abc".to_string(), bind_library::ValueType::Number),
531        );
532
533        assert_eq!(
534            compile_statements(rules.statements, &symbol_table, false).unwrap(),
535            vec![
536                SymbolicInstructionInfo {
537                    location: Some(AstLocation::ConditionStatement(condition_statement)),
538                    instruction: SymbolicInstruction::AbortIfNotEqual {
539                        lhs: Symbol::Key("abc".to_string(), bind_library::ValueType::Number),
540                        rhs: Symbol::NumberValue(42)
541                    }
542                },
543                SymbolicInstructionInfo {
544                    location: None,
545                    instruction: SymbolicInstruction::UnconditionalBind
546                }
547            ]
548        );
549    }
550
551    #[test]
552    fn accept() {
553        let rules = bind_rules::Ast {
554            using: vec![],
555            statements: vec![Statement::Accept {
556                span: Span::new(),
557                identifier: make_identifier!("abc"),
558                values: vec![Value::NumericLiteral(42), Value::NumericLiteral(314)],
559            }],
560        };
561        let mut symbol_table = HashMap::new();
562        symbol_table.insert(
563            make_identifier!("abc"),
564            Symbol::Key("abc".to_string(), bind_library::ValueType::Number),
565        );
566
567        assert_eq!(
568            compile_statements(rules.statements, &symbol_table, false).unwrap(),
569            vec![
570                SymbolicInstructionInfo {
571                    location: Some(AstLocation::AcceptStatementValue {
572                        identifier: make_identifier!("abc"),
573                        value: Value::NumericLiteral(42),
574                        span: Span::new()
575                    }),
576                    instruction: SymbolicInstruction::JumpIfEqual {
577                        lhs: Symbol::Key("abc".to_string(), bind_library::ValueType::Number),
578                        rhs: Symbol::NumberValue(42),
579                        label: 0
580                    }
581                },
582                SymbolicInstructionInfo {
583                    location: Some(AstLocation::AcceptStatementValue {
584                        identifier: make_identifier!("abc"),
585                        value: Value::NumericLiteral(314),
586                        span: Span::new()
587                    }),
588                    instruction: SymbolicInstruction::JumpIfEqual {
589                        lhs: Symbol::Key("abc".to_string(), bind_library::ValueType::Number),
590                        rhs: Symbol::NumberValue(314),
591                        label: 0
592                    }
593                },
594                SymbolicInstructionInfo {
595                    location: Some(AstLocation::AcceptStatementFailure {
596                        identifier: make_identifier!("abc"),
597                        symbol: Symbol::Key("abc".to_string(), bind_library::ValueType::Number),
598                        span: Span::new()
599                    }),
600                    instruction: SymbolicInstruction::UnconditionalAbort
601                },
602                SymbolicInstructionInfo {
603                    location: None,
604                    instruction: SymbolicInstruction::Label(0)
605                },
606                SymbolicInstructionInfo {
607                    location: None,
608                    instruction: SymbolicInstruction::UnconditionalBind
609                },
610            ]
611        );
612    }
613
614    #[test]
615    fn if_else() {
616        let condition1 = Condition {
617            span: Span::new(),
618            lhs: make_identifier!("abc"),
619            op: ConditionOp::Equals,
620            rhs: Value::NumericLiteral(1),
621        };
622        let condition2 = Condition {
623            span: Span::new(),
624            lhs: make_identifier!("abc"),
625            op: ConditionOp::Equals,
626            rhs: Value::NumericLiteral(2),
627        };
628        let statement1 = Statement::ConditionStatement {
629            span: Span::new(),
630            condition: Condition {
631                span: Span::new(),
632                lhs: make_identifier!("abc"),
633                op: ConditionOp::Equals,
634                rhs: Value::NumericLiteral(2),
635            },
636        };
637        let statement2 = Statement::ConditionStatement {
638            span: Span::new(),
639            condition: Condition {
640                span: Span::new(),
641                lhs: make_identifier!("abc"),
642                op: ConditionOp::Equals,
643                rhs: Value::NumericLiteral(3),
644            },
645        };
646        let statement3 = Statement::ConditionStatement {
647            span: Span::new(),
648            condition: Condition {
649                span: Span::new(),
650                lhs: make_identifier!("abc"),
651                op: ConditionOp::Equals,
652                rhs: Value::NumericLiteral(3),
653            },
654        };
655
656        let rules = bind_rules::Ast {
657            using: vec![],
658            statements: vec![Statement::If {
659                span: Span::new(),
660                blocks: vec![
661                    (condition1.clone(), vec![statement1.clone()]),
662                    (condition2.clone(), vec![statement2.clone()]),
663                ],
664                else_block: vec![statement3.clone()],
665            }],
666        };
667        let mut symbol_table = HashMap::new();
668        symbol_table.insert(
669            make_identifier!("abc"),
670            Symbol::Key("abc".to_string(), bind_library::ValueType::Number),
671        );
672
673        assert_eq!(
674            compile_statements(rules.statements, &symbol_table, false).unwrap(),
675            vec![
676                SymbolicInstructionInfo {
677                    location: Some(AstLocation::IfCondition(condition1)),
678                    instruction: SymbolicInstruction::JumpIfNotEqual {
679                        lhs: Symbol::Key("abc".to_string(), bind_library::ValueType::Number),
680                        rhs: Symbol::NumberValue(1),
681                        label: 1
682                    }
683                },
684                SymbolicInstructionInfo {
685                    location: Some(AstLocation::ConditionStatement(statement1)),
686                    instruction: SymbolicInstruction::AbortIfNotEqual {
687                        lhs: Symbol::Key("abc".to_string(), bind_library::ValueType::Number),
688                        rhs: Symbol::NumberValue(2)
689                    }
690                },
691                SymbolicInstructionInfo {
692                    location: None,
693                    instruction: SymbolicInstruction::UnconditionalJump { label: 0 }
694                },
695                SymbolicInstructionInfo {
696                    location: None,
697                    instruction: SymbolicInstruction::Label(1)
698                },
699                SymbolicInstructionInfo {
700                    location: Some(AstLocation::IfCondition(condition2)),
701                    instruction: SymbolicInstruction::JumpIfNotEqual {
702                        lhs: Symbol::Key("abc".to_string(), bind_library::ValueType::Number),
703                        rhs: Symbol::NumberValue(2),
704                        label: 2
705                    }
706                },
707                SymbolicInstructionInfo {
708                    location: Some(AstLocation::ConditionStatement(statement2)),
709                    instruction: SymbolicInstruction::AbortIfNotEqual {
710                        lhs: Symbol::Key("abc".to_string(), bind_library::ValueType::Number),
711                        rhs: Symbol::NumberValue(3)
712                    }
713                },
714                SymbolicInstructionInfo {
715                    location: None,
716                    instruction: SymbolicInstruction::UnconditionalJump { label: 0 }
717                },
718                SymbolicInstructionInfo {
719                    location: None,
720                    instruction: SymbolicInstruction::Label(2)
721                },
722                SymbolicInstructionInfo {
723                    location: Some(AstLocation::ConditionStatement(statement3)),
724                    instruction: SymbolicInstruction::AbortIfNotEqual {
725                        lhs: Symbol::Key("abc".to_string(), bind_library::ValueType::Number),
726                        rhs: Symbol::NumberValue(3)
727                    }
728                },
729                SymbolicInstructionInfo {
730                    location: None,
731                    instruction: SymbolicInstruction::Label(0)
732                },
733                SymbolicInstructionInfo {
734                    location: None,
735                    instruction: SymbolicInstruction::UnconditionalBind
736                },
737            ]
738        );
739    }
740
741    #[test]
742    fn if_else_must_be_terminal() {
743        let rules = bind_rules::Ast {
744            using: vec![],
745            statements: vec![
746                Statement::If {
747                    span: Span::new(),
748                    blocks: vec![(
749                        Condition {
750                            span: Span::new(),
751                            lhs: make_identifier!("abc"),
752                            op: ConditionOp::Equals,
753                            rhs: Value::NumericLiteral(1),
754                        },
755                        vec![Statement::ConditionStatement {
756                            span: Span::new(),
757                            condition: Condition {
758                                span: Span::new(),
759                                lhs: make_identifier!("abc"),
760                                op: ConditionOp::Equals,
761                                rhs: Value::NumericLiteral(2),
762                            },
763                        }],
764                    )],
765                    else_block: vec![Statement::ConditionStatement {
766                        span: Span::new(),
767                        condition: Condition {
768                            span: Span::new(),
769                            lhs: make_identifier!("abc"),
770                            op: ConditionOp::Equals,
771                            rhs: Value::NumericLiteral(3),
772                        },
773                    }],
774                },
775                Statement::Accept {
776                    span: Span::new(),
777                    identifier: make_identifier!("abc"),
778                    values: vec![Value::NumericLiteral(42), Value::NumericLiteral(314)],
779                },
780            ],
781        };
782        let mut symbol_table = HashMap::new();
783        symbol_table.insert(
784            make_identifier!("abc"),
785            Symbol::Key("abc".to_string(), bind_library::ValueType::Number),
786        );
787
788        assert_eq!(
789            compile_statements(rules.statements, &symbol_table, false),
790            Err(CompilerError::IfStatementMustBeTerminal)
791        );
792    }
793
794    #[test]
795    fn false_statement() {
796        let abort_statement = Statement::False { span: Span::new() };
797
798        let rules = bind_rules::Ast { using: vec![], statements: vec![abort_statement.clone()] };
799        let symbol_table = HashMap::new();
800
801        assert_eq!(
802            compile_statements(rules.statements, &symbol_table, false).unwrap(),
803            vec![
804                SymbolicInstructionInfo {
805                    location: Some(AstLocation::FalseStatement(abort_statement)),
806                    instruction: SymbolicInstruction::UnconditionalAbort
807                },
808                SymbolicInstructionInfo {
809                    location: None,
810                    instruction: SymbolicInstruction::UnconditionalBind
811                }
812            ]
813        );
814    }
815
816    #[test]
817    fn false_statement_must_be_isolated() {
818        let condition_statement = Statement::ConditionStatement {
819            span: Span::new(),
820            condition: Condition {
821                span: Span::new(),
822                lhs: make_identifier!("abc"),
823                op: ConditionOp::Equals,
824                rhs: Value::NumericLiteral(42),
825            },
826        };
827        let abort_statement = Statement::False { span: Span::new() };
828
829        let rules = bind_rules::Ast {
830            using: vec![],
831            statements: vec![condition_statement.clone(), abort_statement.clone()],
832        };
833        let mut symbol_table = HashMap::new();
834        symbol_table.insert(
835            make_identifier!("abc"),
836            Symbol::Key("abc".to_string(), bind_library::ValueType::Number),
837        );
838
839        assert_eq!(
840            compile_statements(rules.statements, &symbol_table, false),
841            Err(CompilerError::FalseStatementMustBeIsolated)
842        );
843    }
844
845    #[test]
846    fn true_statement_must_be_isolated() {
847        let condition_statement = Statement::ConditionStatement {
848            span: Span::new(),
849            condition: Condition {
850                span: Span::new(),
851                lhs: make_identifier!("abc"),
852                op: ConditionOp::Equals,
853                rhs: Value::NumericLiteral(42),
854            },
855        };
856        let abort_statement = Statement::True { span: Span::new() };
857
858        let rules = bind_rules::Ast {
859            using: vec![],
860            statements: vec![condition_statement.clone(), abort_statement.clone()],
861        };
862        let mut symbol_table = HashMap::new();
863        symbol_table.insert(
864            make_identifier!("abc"),
865            Symbol::Key("abc".to_string(), bind_library::ValueType::Number),
866        );
867
868        assert_eq!(
869            compile_statements(rules.statements, &symbol_table, false),
870            Err(CompilerError::TrueStatementMustBeIsolated)
871        );
872    }
873
874    #[test]
875    fn dependencies() {
876        let rules = bind_rules::Ast {
877            using: vec![Include { name: make_identifier!("A"), alias: None }],
878            statements: vec![],
879        };
880        let libraries = vec![
881            bind_library::Ast {
882                name: make_identifier!("A"),
883                using: vec![Include { name: make_identifier!("A", "B"), alias: None }],
884                declarations: vec![],
885            },
886            bind_library::Ast {
887                name: make_identifier!("A", "B"),
888                using: vec![],
889                declarations: vec![],
890            },
891            bind_library::Ast {
892                name: make_identifier!("A", "C"),
893                using: vec![],
894                declarations: vec![],
895            },
896        ];
897
898        assert_eq!(
899            resolve_dependencies(&rules.using, libraries.iter()),
900            Ok(vec![
901                &bind_library::Ast {
902                    name: make_identifier!("A"),
903                    using: vec![Include { name: make_identifier!("A", "B"), alias: None }],
904                    declarations: vec![],
905                },
906                &bind_library::Ast {
907                    name: make_identifier!("A", "B"),
908                    using: vec![],
909                    declarations: vec![],
910                },
911            ])
912        );
913    }
914
915    #[test]
916    fn dependencies_error() {
917        let rules = bind_rules::Ast {
918            using: vec![Include { name: make_identifier!("A"), alias: None }],
919            statements: vec![],
920        };
921        let libraries = vec![
922            bind_library::Ast {
923                name: make_identifier!("A"),
924                using: vec![Include { name: make_identifier!("A", "B"), alias: None }],
925                declarations: vec![],
926            },
927            bind_library::Ast {
928                name: make_identifier!("A", "C"),
929                using: vec![],
930                declarations: vec![],
931            },
932        ];
933
934        assert_eq!(
935            resolve_dependencies(&rules.using, libraries.iter()),
936            Err(CompilerError::DependencyError(
937                dependency_graph::DependencyError::MissingDependency(make_identifier!("A", "B"))
938            ))
939        );
940    }
941
942    #[test]
943    fn uncondition_bind_in_new_bytecode() {
944        let condition_statement = Statement::ConditionStatement {
945            span: Span::new(),
946            condition: Condition {
947                span: Span::new(),
948                lhs: make_identifier!("wheatear"),
949                op: ConditionOp::Equals,
950                rhs: Value::NumericLiteral(8),
951            },
952        };
953
954        let rules =
955            bind_rules::Ast { using: vec![], statements: vec![condition_statement.clone()] };
956        let mut symbol_table = HashMap::new();
957        symbol_table.insert(
958            make_identifier!("wheatear"),
959            Symbol::Key("wheatear".to_string(), bind_library::ValueType::Number),
960        );
961
962        // Instructions should not contain unconditional bind.
963        assert_eq!(
964            compile_statements(rules.statements, &symbol_table, true).unwrap(),
965            vec![SymbolicInstructionInfo {
966                location: Some(AstLocation::ConditionStatement(condition_statement)),
967                instruction: SymbolicInstruction::AbortIfNotEqual {
968                    lhs: Symbol::Key("wheatear".to_string(), bind_library::ValueType::Number),
969                    rhs: Symbol::NumberValue(8)
970                }
971            },]
972        );
973    }
974}