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::{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 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 !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 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 self.compile_block(block_statements)?;
456
457 self.instructions.push(SymbolicInstructionInfo {
459 location: None,
460 instruction: SymbolicInstruction::UnconditionalJump {
461 label: final_label_id,
462 },
463 });
464
465 self.instructions.push(SymbolicInstructionInfo {
467 location: None,
468 instruction: SymbolicInstruction::Label(label_id),
469 });
470 }
471
472 self.compile_block(else_block)?;
474
475 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 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 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}