diff --git a/src/compiler/mod.rs b/src/compiler/mod.rs index 6f5c945..98e350e 100644 --- a/src/compiler/mod.rs +++ b/src/compiler/mod.rs @@ -57,6 +57,7 @@ pub enum Instruction { LoadFromContextToSlot { name: NomoInput, slot: VariableSlot, + fail_on_not_found: bool, }, EmitFromSlot { slot: VariableSlot, @@ -440,6 +441,14 @@ fn emit_expr_load( eval.push(Instruction::LoadFromContextToSlot { name: template_token.source().clone(), slot: emit_slot, + fail_on_not_found: true, + }); + } + TemplateAstExpr::ConditionalAccess(template_token) => { + eval.push(Instruction::LoadFromContextToSlot { + name: template_token.source().clone(), + slot: emit_slot, + fail_on_not_found: false, }); } TemplateAstExpr::Literal { source, value } => { @@ -476,7 +485,6 @@ fn emit_expr_load( slot: emit_slot, }); } - TemplateAstExpr::ConditionalAccess { .. } => todo!(), TemplateAstExpr::Invalid { .. } => eval.push(Instruction::Abort), TemplateAstExpr::StaticContent { .. } | TemplateAstExpr::Interpolation { .. } => { unreachable!("Invalid AST here") @@ -523,6 +531,7 @@ mod tests { slot: VariableSlot { index: 0, }, + fail_on_not_found: true, }, EmitFromSlot { slot: VariableSlot { diff --git a/src/compiler/snapshots/nomo__compiler__tests__check_if_else_if.snap b/src/compiler/snapshots/nomo__compiler__tests__check_if_else_if.snap index de57cc6..5cf26c0 100644 --- a/src/compiler/snapshots/nomo__compiler__tests__check_if_else_if.snap +++ b/src/compiler/snapshots/nomo__compiler__tests__check_if_else_if.snap @@ -20,6 +20,7 @@ VMInstructions { slot: VariableSlot { index: 1, }, + fail_on_not_found: true, }, JumpIfNotTrue { emit_slot: VariableSlot { @@ -51,6 +52,7 @@ VMInstructions { slot: VariableSlot { index: 3, }, + fail_on_not_found: true, }, JumpIfNotTrue { emit_slot: VariableSlot { diff --git a/src/eval/mod.rs b/src/eval/mod.rs index 96ae66e..62a013f 100644 --- a/src/eval/mod.rs +++ b/src/eval/mod.rs @@ -93,15 +93,34 @@ pub fn execute( match instr { Instruction::NoOp => (), Instruction::AppendContent { content } => output.push_str(content), - Instruction::LoadFromContextToSlot { name, slot } => { - let value = scopes - .get_scoped(name) - .ok_or(EvaluationError::UnknownVariable(name.clone()))?; + Instruction::LoadFromContextToSlot { + name, + slot, + fail_on_not_found, + } => { + let value = scopes.get_scoped(name); + + let value = if let Some(val) = value { + val + } else { + if *fail_on_not_found { + return Err(EvaluationError::UnknownVariable(name.clone())); + } else { + &NomoValue::Undefined + } + }; scopes.insert_into_slot(*slot, value.clone()); } Instruction::EmitFromSlot { slot } => { - let value = scopes.get(slot).try_to_string().unwrap(); + let value = scopes.get(slot); + let value = if let Some(value) = value.try_to_string() { + value + } else if matches!(value, NomoValue::Undefined) { + String::new() + } else { + panic!("Unknown variable"); + }; output.push_str(&value); } @@ -286,4 +305,26 @@ mod tests { ) "#); } + + #[test] + fn check_conditional_access() { + let input = "Hello {{= unknown? }}"; + + let parsed = crate::lexer::parse(input.into()).unwrap(); + + let ast = crate::parser::parse(parsed.tokens()).unwrap(); + + let emit = crate::compiler::emit_machine(ast); + + let context = Context::new(); + let function_map = FunctionMap::default(); + + let output = execute(&function_map, &emit, &context); + + insta::assert_debug_snapshot!(output, @r#" + Ok( + "Hello ", + ) + "#); + } } diff --git a/tests/cases/condition.3-instructions.snap b/tests/cases/condition.3-instructions.snap index a317547..50c3a5f 100644 --- a/tests/cases/condition.3-instructions.snap +++ b/tests/cases/condition.3-instructions.snap @@ -22,6 +22,7 @@ VMInstructions { slot: VariableSlot { index: 1, }, + fail_on_not_found: true, }, JumpIfNotTrue { emit_slot: VariableSlot { @@ -56,6 +57,7 @@ VMInstructions { slot: VariableSlot { index: 3, }, + fail_on_not_found: true, }, EmitFromSlot { slot: VariableSlot { diff --git a/tests/cases/identifiers.3-instructions.snap b/tests/cases/identifiers.3-instructions.snap index f3d8ddc..6d13773 100644 --- a/tests/cases/identifiers.3-instructions.snap +++ b/tests/cases/identifiers.3-instructions.snap @@ -4,12 +4,12 @@ expression: emit info: input: "{{= _name }}\n{{= a_name }}\n{{= name }}\n{{= _name1 }}\n{{= _namE }}\n{{= name1 }}" context: - name1: Foo - _name: Foo - _namE: Foo a_name: Foo - name: Foo + _name: Foo _name1: Foo + _namE: Foo + name1: Foo + name: Foo --- VMInstructions { labels: {}, @@ -19,6 +19,7 @@ VMInstructions { slot: VariableSlot { index: 0, }, + fail_on_not_found: true, }, EmitFromSlot { slot: VariableSlot { @@ -33,6 +34,7 @@ VMInstructions { slot: VariableSlot { index: 1, }, + fail_on_not_found: true, }, EmitFromSlot { slot: VariableSlot { @@ -47,6 +49,7 @@ VMInstructions { slot: VariableSlot { index: 2, }, + fail_on_not_found: true, }, EmitFromSlot { slot: VariableSlot { @@ -61,6 +64,7 @@ VMInstructions { slot: VariableSlot { index: 3, }, + fail_on_not_found: true, }, EmitFromSlot { slot: VariableSlot { @@ -75,6 +79,7 @@ VMInstructions { slot: VariableSlot { index: 4, }, + fail_on_not_found: true, }, EmitFromSlot { slot: VariableSlot { @@ -89,6 +94,7 @@ VMInstructions { slot: VariableSlot { index: 5, }, + fail_on_not_found: true, }, EmitFromSlot { slot: VariableSlot { diff --git a/tests/cases/if_else_if.3-instructions.snap b/tests/cases/if_else_if.3-instructions.snap index 5504ab9..338a105 100644 --- a/tests/cases/if_else_if.3-instructions.snap +++ b/tests/cases/if_else_if.3-instructions.snap @@ -26,6 +26,7 @@ VMInstructions { slot: VariableSlot { index: 1, }, + fail_on_not_found: true, }, JumpIfNotTrue { emit_slot: VariableSlot { @@ -57,6 +58,7 @@ VMInstructions { slot: VariableSlot { index: 3, }, + fail_on_not_found: true, }, JumpIfNotTrue { emit_slot: VariableSlot { diff --git a/tests/cases/interpolation.3-instructions.snap b/tests/cases/interpolation.3-instructions.snap index c47d18a..4465366 100644 --- a/tests/cases/interpolation.3-instructions.snap +++ b/tests/cases/interpolation.3-instructions.snap @@ -20,6 +20,7 @@ VMInstructions { slot: VariableSlot { index: 0, }, + fail_on_not_found: true, }, EmitFromSlot { slot: VariableSlot { diff --git a/tests/cases/multiple.3-instructions.snap b/tests/cases/multiple.3-instructions.snap index ef538c1..2b475e7 100644 --- a/tests/cases/multiple.3-instructions.snap +++ b/tests/cases/multiple.3-instructions.snap @@ -21,6 +21,7 @@ VMInstructions { slot: VariableSlot { index: 0, }, + fail_on_not_found: true, }, EmitFromSlot { slot: VariableSlot { @@ -35,6 +36,7 @@ VMInstructions { slot: VariableSlot { index: 1, }, + fail_on_not_found: true, }, EmitFromSlot { slot: VariableSlot { diff --git a/tests/cases/simple_for.3-instructions.snap b/tests/cases/simple_for.3-instructions.snap index f082c47..415d516 100644 --- a/tests/cases/simple_for.3-instructions.snap +++ b/tests/cases/simple_for.3-instructions.snap @@ -39,6 +39,7 @@ VMInstructions { slot: VariableSlot { index: 4, }, + fail_on_not_found: true, }, CreateIteratorFromSlotToSlot { iterator_slot: VariableSlot { @@ -78,6 +79,7 @@ VMInstructions { slot: VariableSlot { index: 6, }, + fail_on_not_found: true, }, EmitFromSlot { slot: VariableSlot { @@ -104,6 +106,7 @@ VMInstructions { slot: VariableSlot { index: 11, }, + fail_on_not_found: true, }, CreateIteratorFromSlotToSlot { iterator_slot: VariableSlot { @@ -143,6 +146,7 @@ VMInstructions { slot: VariableSlot { index: 13, }, + fail_on_not_found: true, }, EmitFromSlot { slot: VariableSlot { diff --git a/tests/cases/trim_whitespace.3-instructions.snap b/tests/cases/trim_whitespace.3-instructions.snap index 31112d0..f484bbf 100644 --- a/tests/cases/trim_whitespace.3-instructions.snap +++ b/tests/cases/trim_whitespace.3-instructions.snap @@ -4,8 +4,8 @@ expression: emit info: input: "{{ if test -}}\n Hello {{= stuff -}}\n{{- end }}" context: - stuff: Hemera test: true + stuff: Hemera --- VMInstructions { labels: { @@ -22,6 +22,7 @@ VMInstructions { slot: VariableSlot { index: 1, }, + fail_on_not_found: true, }, JumpIfNotTrue { emit_slot: VariableSlot { @@ -42,6 +43,7 @@ VMInstructions { slot: VariableSlot { index: 3, }, + fail_on_not_found: true, }, EmitFromSlot { slot: VariableSlot {