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