Semantic Analyzer for a Simple Expression Language
A semantic analyzer verifies the meaning and consistency of code, ensuring it adheres to the rules of a programming language. This challenge asks you to implement a basic semantic analyzer for a simplified expression language, focusing on variable declaration and usage. This is a crucial step in compiler design and helps catch errors before runtime.
Problem Description
You are tasked with building a semantic analyzer that checks a series of JavaScript-like statements for semantic correctness. The language supports variable declarations (using let) and simple assignments. The analyzer should verify that:
- Variables are declared before use: A variable must be declared using
letbefore it is assigned a value or used in an expression. - No duplicate declarations: A variable cannot be declared more than once within the same scope (for simplicity, assume a single global scope).
- Valid Assignment: Assignments should only occur to declared variables.
The input will be an array of strings, where each string represents a statement. The statements will consist of let declarations, variable assignments, and simple expressions using declared variables. The analyzer should return an array of error messages. If no errors are found, return an empty array.
Expected Behavior:
The analyzer should iterate through the statements and maintain a set of declared variables. For each statement, it should check if the statement is a declaration, an assignment, or an expression. If it's a declaration, add the variable to the set. If it's an assignment or expression, check if the variable exists in the set. If an error is detected, add a descriptive error message to the error array.
Edge Cases to Consider:
- Empty input array.
- Statements with invalid syntax (though you don't need to parse the syntax, assume the statements are well-formed enough to identify variable names).
- Variables used in expressions before assignment.
- Multiple assignments to the same variable.
- Variables declared and then immediately used in the same statement.
Examples
Example 1:
Input: ["let x = 10;", "let y = x + 5;", "console.log(y);"]
Output: []
Explanation: All variables are declared before use, and there are no duplicate declarations.
Example 2:
Input: ["let x = 10;", "console.log(y);", "let y = x + 5;"]
Output: ["Error: Variable 'y' used before declaration."]
Explanation: Variable 'y' is used before it is declared.
Example 3:
Input: ["let x = 10;", "let x = 20;", "let y = x + 5;"]
Output: ["Error: Duplicate declaration of variable 'x'."]
Explanation: Variable 'x' is declared twice.
Example 4:
Input: ["let x = 10;", "z = x + 5;"]
Output: ["Error: Variable 'z' used before declaration."]
Explanation: Variable 'z' is used without being declared.
Example 5:
Input: []
Output: []
Explanation: Empty input, no errors.
Constraints
- Input Size: The input array will contain at most 100 statements.
- Statement Length: Each statement will be a string with a maximum length of 200 characters.
- Variable Names: Variable names will consist of alphanumeric characters (a-z, A-Z, 0-9) and underscores (_).
- Performance: The analyzer should complete within 0.5 seconds for the given input size.
- Error Messages: Error messages should be clear and descriptive, indicating the type of error and the variable involved.
Notes
- You don't need to implement a full-fledged parser. Assume the input statements are well-formed enough to extract variable names and identify declarations, assignments, and expressions. Focus on the semantic analysis logic.
- Consider using a
Setto efficiently track declared variables. - The expressions are simple and do not need to be evaluated. You only need to identify the variables used within them.
- The scope is global for simplicity. You don't need to handle nested scopes.
- Assume that
letis the only declaration keyword. - Focus on the core semantic analysis logic; error handling for invalid syntax is not required.