1use crate::bytecode_encoder::encode_v1::encode_to_bytecode_v1;
6use crate::bytecode_encoder::encode_v2::{encode_composite_to_bytecode, encode_to_bytecode_v2};
7use crate::bytecode_encoder::error::BindRulesEncodeError;
8use crate::compiler::symbol_table::*;
9use crate::compiler::{dependency_graph, instruction};
10use crate::ddk_bind_constants::BIND_AUTOBIND;
11use crate::debugger::offline_debugger::AstLocation;
12use crate::errors::UserError;
13use crate::linter;
14use crate::parser::bind_rules::{self, Condition, ConditionOp, Statement};
15use crate::parser::common::{BindParserError, CompoundIdentifier, Value};
16use crate::parser::{self, bind_composite};
17use std::collections::HashMap;
18use std::fmt;
19use thiserror::Error;
20
21#[derive(Debug, Error, Clone, PartialEq)]
22pub enum CompilerError {
23 BindParserError(parser::common::BindParserError),
24 DependencyError(dependency_graph::DependencyError<CompoundIdentifier>),
25 LinterError(linter::LinterError),
26 DuplicateIdentifier(CompoundIdentifier),
27 TypeMismatch(CompoundIdentifier),
28 UnresolvedQualification(CompoundIdentifier),
29 UndeclaredKey(CompoundIdentifier),
30 MissingExtendsKeyword(CompoundIdentifier),
31 InvalidExtendsKeyword(CompoundIdentifier),
32 UnknownKey(CompoundIdentifier),
33 IfStatementMustBeTerminal,
34 TrueStatementMustBeIsolated,
35 FalseStatementMustBeIsolated,
36}
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#[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 }
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 !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 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 self.compile_block(block_statements)?;
464
465 self.instructions.push(SymbolicInstructionInfo {
467 location: None,
468 instruction: SymbolicInstruction::UnconditionalJump {
469 label: final_label_id,
470 },
471 });
472
473 self.instructions.push(SymbolicInstructionInfo {
475 location: None,
476 instruction: SymbolicInstruction::Label(label_id),
477 });
478 }
479
480 self.compile_block(else_block)?;
482
483 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 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 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}