THE ROLE OF INTERPRETERS IN PROGRAMMING LANGUAGE IMPLEMENTATION
Interpreters represent one of the fundamental approaches to programming language implementation, offering a direct execution model that contrasts with the compilation approach we explored in previous articles. While compilers transform source code into machine code for later execution, interpreters execute source code directly by traversing and evaluating the program's abstract syntax tree in real-time.
The choice between interpretation and compilation involves important trade-offs that affect development workflow, debugging capabilities, performance characteristics, and deployment strategies. Interpreters excel in scenarios requiring rapid prototyping, interactive development, dynamic code evaluation, and educational environments where understanding program execution is paramount.
For the PyGo programming language, implementing both a compiler and an interpreter provides developers with flexible execution options. The compiler enables efficient production deployment with optimized machine code, while the interpreter supports rapid development cycles, interactive programming, and detailed runtime introspection.
INTERPRETER ARCHITECTURE AND DESIGN PRINCIPLES
The PyGo interpreter employs a tree-walking interpretation strategy, directly executing abstract syntax tree nodes according to the language's operational semantics. This approach provides clear correspondence between source code constructs and their runtime behavior, making the interpreter both understandable and maintainable.
The interpreter architecture consists of several interconnected components that work together to provide complete program execution capabilities:
Runtime Value System: A comprehensive type system that represents all PyGo data types during execution, including integers, floats, strings, booleans, functions, and special values like void. Each runtime value encapsulates both data and type information, enabling dynamic type checking and automatic conversions.
Environment Management: A sophisticated scoping system that manages variable bindings, function definitions, and nested execution contexts. The environment system supports lexical scoping, variable shadowing, closure capture, and proper cleanup of execution contexts.
Expression Evaluation Engine: A complete implementation of PyGo's expression semantics, including arithmetic operations, logical operations, comparison operations, and function calls. The evaluation engine handles operator precedence, type coercion, and error conditions.
Control Flow Management: Implementation of all PyGo control structures including conditional statements, loops, function calls, and return statements. The control flow system uses exception-based mechanisms to handle non-local control transfers.
Built-in Function Library: A collection of essential functions that provide core language capabilities such as input/output operations, type conversions, and utility functions. Built-in functions integrate seamlessly with user-defined functions.
Error Handling and Debugging: Comprehensive error detection, reporting, and recovery mechanisms that provide detailed information about runtime errors, including location information, context, and suggested corrections.
TREE-WALKING INTERPRETATION METHODOLOGY
Tree-walking interpretation operates by recursively traversing the abstract syntax tree and executing each node according to its semantic meaning. This approach directly implements the operational semantics of the programming language, making the interpreter's behavior predictable and debuggable.
The interpretation process begins with a parsed abstract syntax tree representing the complete program. The interpreter visits each node using the visitor pattern, dispatching to appropriate handler methods based on the node type. Each handler method implements the runtime semantics for its corresponding language construct.
For expression nodes, the interpreter evaluates operands recursively and applies the appropriate operation to produce a result value. For statement nodes, the interpreter executes the statement's effects, which may include variable assignments, control flow changes, or function calls.
The tree-walking approach provides several advantages for the PyGo interpreter:
Simplicity and Clarity: The direct correspondence between AST nodes and execution logic makes the interpreter easy to understand and modify. Each language construct has a clear implementation that directly reflects its intended semantics.
Debugging and Introspection: The interpreter can provide detailed information about program execution, including variable values, call stacks, and execution traces. This capability is invaluable for debugging and educational purposes.
Flexibility and Extensibility: Adding new language features requires only implementing new AST node types and their corresponding interpretation logic. The modular design supports incremental language development.
Error Handling: Runtime errors can be detected and reported with precise location information and context. The interpreter can provide helpful error messages and recovery suggestions.
RUNTIME VALUE REPRESENTATION AND TYPE SYSTEM
The PyGo interpreter implements a sophisticated runtime value system that represents all language data types during execution. Unlike compiled languages where types are primarily compile-time constructs, interpreted languages must maintain type information at runtime to support dynamic operations and error checking.
Each runtime value in the PyGo interpreter consists of two components: the actual data and associated type information. This design enables the interpreter to perform type checking during expression evaluation, implement automatic type conversions, and provide meaningful error messages for type-related errors.
The value system supports all PyGo data types:
Integer Values: Represent 32-bit signed integers with support for arithmetic operations, comparisons, and conversions to other numeric types. Integer values handle overflow conditions and provide appropriate error messages for invalid operations.
Float Values: Represent double-precision floating-point numbers with proper handling of special values like infinity and NaN. Float operations implement IEEE 754 semantics and provide appropriate precision for mathematical computations.
String Values: Represent Unicode text strings with support for concatenation, comparison, and length operations. String values are immutable and provide efficient storage and manipulation capabilities.
Boolean Values: Represent logical true/false values with support for logical operations and truthiness testing. Boolean values integrate with conditional statements and logical expressions.
Function Values: Represent both user-defined and built-in functions with support for parameter binding, closure capture, and recursive calls. Function values encapsulate the function's definition, parameter list, and closure environment.
Void Values: Represent the absence of a meaningful value, used for functions that don't return data and for uninitialized variables.
The runtime value system implements automatic type conversions following PyGo's type compatibility rules. These conversions enable natural operations between compatible types while maintaining type safety through runtime checking.
ENVIRONMENT MANAGEMENT AND SCOPING
The PyGo interpreter implements a comprehensive environment system that manages variable bindings and function definitions throughout program execution. The environment system supports lexical scoping, where variable visibility is determined by the program's static structure rather than dynamic execution context.
Each environment represents a scope containing variable bindings and references to parent scopes. The interpreter creates new environments for function calls, block statements, and loop constructs, ensuring proper variable isolation and cleanup.
The environment system supports several key operations:
Variable Definition: Creating new variable bindings in the current scope with appropriate type checking and duplicate detection. Variable definitions are permanent within their scope and cannot be redefined.
Variable Assignment: Modifying existing variable values with type compatibility checking. Assignments search through the scope chain to find the appropriate binding location.
Variable Lookup: Retrieving variable values by searching through the scope chain from innermost to outermost scope. Lookup operations handle undefined variables with appropriate error messages.
Scope Management: Creating and destroying scopes as execution progresses through different program constructs. Scope management ensures proper cleanup and prevents memory leaks.
The environment system implements closure capture for function definitions, allowing functions to access variables from their defining scope even when called from different contexts. This capability is essential for supporting higher-order functions and functional programming patterns.
EXPRESSION EVALUATION AND OPERATOR SEMANTICS
The expression evaluation engine forms the core computational component of the PyGo interpreter, implementing the runtime semantics for all PyGo expressions. The evaluation engine handles operator precedence, type checking, automatic conversions, and error conditions while maintaining compatibility with PyGo's language specification.
Expression evaluation follows a recursive descent approach where complex expressions are broken down into simpler components. Each expression type has a dedicated evaluation method that implements its specific semantics:
Arithmetic Expressions: Implement mathematical operations with proper type handling and overflow detection. Arithmetic operations support both integer and floating-point arithmetic with automatic promotion rules.
Comparison Expressions: Implement relational operations between compatible types with proper handling of floating-point precision and string comparison semantics.
Logical Expressions: Implement boolean operations with short-circuit evaluation for efficiency and correctness. Logical operations support truthiness testing for non-boolean values.
Function Call Expressions: Implement function invocation with parameter binding, argument type checking, and return value handling. Function calls support both user-defined and built-in functions.
Identifier Expressions: Implement variable lookup with scope resolution and undefined variable detection. Identifier resolution follows lexical scoping rules.
The evaluation engine implements operator precedence through the parser's expression hierarchy, ensuring that operations are performed in the correct order. Type checking occurs at runtime, providing immediate feedback about type errors with detailed context information.
CONTROL FLOW IMPLEMENTATION AND EXCEPTION HANDLING
The PyGo interpreter implements all language control structures using a combination of direct execution and exception-based control flow. This approach provides clean separation between normal execution and control transfer operations like returns, breaks, and continues.
Control flow implementation covers several key areas:
Conditional Statements: If-else statements evaluate condition expressions and execute appropriate branches based on truthiness. The interpreter supports nested conditionals and proper scope management for each branch.
Loop Statements: While loops and for loops implement iterative execution with support for break and continue statements. Loop implementation includes proper scope management for loop variables and efficient condition evaluation.
Function Calls: Function invocation creates new execution contexts with parameter binding and local variable scopes. Function calls support recursion with stack overflow protection and proper cleanup.
Return Statements: Return statements use exception-based control flow to transfer control back to function callers. Return values are properly typed and validated against function signatures.
The interpreter uses specialized exception types to implement non-local control transfers:
ReturnException: Carries return values from return statements back to function call sites. Return exceptions bypass normal statement execution and provide clean function exit.
BreakException: Implements break statements in loops by transferring control to loop exit points. Break exceptions are caught by loop constructs and ignored by other statement types.
ContinueException: Implements continue statements in loops by transferring control to loop continuation points. Continue exceptions cause loops to skip remaining iteration statements.
This exception-based approach provides clean implementation of control flow while maintaining proper error handling and resource cleanup.
BUILT-IN FUNCTION LIBRARY AND SYSTEM INTEGRATION
The PyGo interpreter includes a comprehensive library of built-in functions that provide essential language capabilities. Built-in functions integrate seamlessly with user-defined functions and support the same calling conventions and error handling mechanisms.
The built-in function library includes several categories of functions:
Input/Output Function: The print function provides formatted output capabilities with support for all PyGo data types. Output functions handle string conversion and formatting automatically.
Type Conversion Functions: Conversion functions enable explicit type conversions between compatible types. These functions provide error handling for invalid conversions and maintain type safety.
Utility Functions: Additional functions provide common operations like string manipulation, mathematical computations, and data structure operations.
Built-in functions are implemented as special function values that contain native code rather than PyGo AST nodes. This approach provides efficient execution while maintaining consistent calling semantics.
The built-in function system is extensible, allowing new functions to be added without modifying the core interpreter. This capability supports library development and domain-specific extensions.
ERROR HANDLING, DEBUGGING, AND DEVELOPMENT SUPPORT
The PyGo interpreter implements comprehensive error handling that provides detailed information about runtime errors while maintaining program stability. Error handling covers several categories of potential problems:
Type Errors: Detected when operations are attempted on incompatible types. Type errors include detailed information about expected and actual types along with suggested corrections.
Runtime Errors: Include division by zero, undefined variables, function call errors, and other runtime conditions. Runtime errors provide context information including line numbers and execution state.
System Errors: Handle resource exhaustion, stack overflow, and other system-level problems. System errors provide graceful degradation and recovery options.
The interpreter includes debugging support that enables detailed program introspection:
Execution Tracing: Optional tracing shows each statement as it executes, providing insight into program flow and variable changes.
Environment Inspection: Debugging commands allow examination of variable bindings and scope hierarchies at any point during execution.
Call Stack Information: Error messages include call stack traces showing the sequence of function calls leading to errors.
Interactive Debugging: The interpreter supports breakpoints, single-stepping, and interactive variable examination for complex debugging scenarios.
PERFORMANCE CONSIDERATIONS AND OPTIMIZATION STRATEGIES
While tree-walking interpreters prioritize simplicity and clarity over raw performance, the PyGo interpreter includes several optimization strategies that improve execution efficiency without compromising maintainability:
Value Caching: Frequently used values like small integers and common strings are cached to reduce allocation overhead. Value caching provides significant performance improvements for numeric computations.
Environment Optimization: Variable lookup uses efficient data structures and caching to minimize scope traversal overhead. Environment optimization is particularly important for deeply nested scopes.
Expression Optimization: Simple expressions like literal values and variable references use optimized evaluation paths that bypass general expression machinery.
Call Optimization: Function calls use specialized handling for built-in functions and tail-recursive calls to reduce overhead and stack usage.
Memory Management: The interpreter implements efficient memory management with automatic cleanup of unused values and environments. Memory management prevents leaks while maintaining good performance.
These optimizations maintain the interpreter's simplicity while providing acceptable performance for development and educational use. For production deployment requiring maximum performance, the compiled version of PyGo provides superior execution speed.
INTEGRATION WITH DEVELOPMENT TOOLS AND ENVIRONMENTS
The PyGo interpreter is designed to integrate seamlessly with development tools and programming environments. This integration support includes several key capabilities:
REPL (Read-Eval-Print Loop): An interactive mode that allows immediate evaluation of PyGo expressions and statements. The REPL supports multi-line input, command history, and context preservation between evaluations.
IDE Integration: The interpreter provides APIs that enable integration with integrated development environments. IDE integration supports features like syntax highlighting, error reporting, and interactive debugging.
Testing Framework Integration: The interpreter supports automated testing frameworks with programmatic execution control and result capture. Testing integration enables comprehensive validation of PyGo programs.
Educational Tools: Special modes support educational use cases including step-by-step execution, visualization of program state, and interactive exploration of language concepts.
The interpreter's modular design facilitates integration with external tools while maintaining clean separation between core interpretation logic and user interface concerns.
COMPARISON WITH COMPILATION APPROACH
The PyGo interpreter complements the compiler implementation by providing different trade-offs and capabilities:
Development Speed: The interpreter enables immediate program execution without compilation delays. This capability accelerates development cycles and supports interactive programming styles.
Debugging Capabilities: Interpretation provides superior debugging support with complete runtime introspection and dynamic modification capabilities. Debugging features are particularly valuable during development.
Portability: The interpreter runs on any platform supporting the Java runtime, eliminating platform-specific compilation requirements. Portability simplifies deployment and distribution.
Resource Usage: Interpretation typically uses more memory and CPU resources than compiled code due to runtime overhead. Resource usage trade-offs must be considered for production deployment.
Error Detection: The interpreter can detect certain classes of errors that might not be caught during compilation. Runtime error detection provides additional safety and reliability.
Flexibility: Interpretation supports dynamic code generation and modification that would be difficult or impossible with static compilation. Flexibility enables advanced programming techniques and tool development.
The combination of interpretation and compilation provides PyGo developers with optimal tools for different phases of software development, from initial prototyping through production deployment.
EDUCATIONAL VALUE AND LEARNING OPPORTUNITIES
The PyGo interpreter serves as an excellent educational tool for understanding programming language implementation concepts. The interpreter's clear structure and comprehensive documentation make it suitable for academic use and self-study:
Language Implementation Concepts: The interpreter demonstrates key concepts including parsing, semantic analysis, runtime systems, and error handling. These concepts are fundamental to understanding how programming languages work.
Algorithm Implementation: The interpreter showcases important algorithms including tree traversal, scope resolution, type checking, and expression evaluation. These algorithms have broad applicability beyond language implementation.
Software Architecture: The interpreter's modular design illustrates good software engineering practices including separation of concerns, interface design, and extensibility planning.
Debugging and Testing: The comprehensive testing framework and debugging capabilities demonstrate best practices for software validation and quality assurance.
Students and practitioners can study the interpreter implementation to gain deep understanding of programming language concepts while working with a complete, functional system.
EXTENSIBILITY AND FUTURE DEVELOPMENT
The PyGo interpreter is designed for extensibility, supporting future language enhancements and experimental features:
Language Feature Addition: New PyGo language features can be added by implementing corresponding AST nodes and interpretation logic. The modular design minimizes the impact of language changes.
Library Development: The built-in function system supports addition of new libraries and domain-specific functions. Library development enables specialized applications and tool integration.
Optimization Opportunities: The interpreter provides a foundation for advanced optimization techniques including bytecode compilation, just-in-time compilation, and adaptive optimization.
Tool Integration: The interpreter's API design supports integration with additional development tools including profilers, analyzers, and visualization systems.
Research Applications: The interpreter serves as a platform for programming language research including experimental features, alternative semantics, and novel optimization strategies.
The interpreter's clean architecture and comprehensive documentation facilitate both practical extensions and research applications.
CONCLUSION OF INTRODUCTION
This comprehensive introduction has explored the design principles, architectural decisions, and implementation strategies that guide the PyGo interpreter development. The interpreter represents a complete alternative execution model for PyGo programs, providing capabilities that complement the compilation approach while serving educational and development needs.
The following sections will present the detailed implementation of each interpreter component, demonstrating how these design principles translate into working code. The implementation showcases modern interpreter construction techniques while maintaining clarity and extensibility.
The PyGo interpreter stands as both a practical tool for PyGo development and an educational resource for understanding programming language implementation. Its comprehensive feature set, robust error handling, and clean architecture make it suitable for both production use and academic study.
Through careful attention to design principles and implementation quality, the PyGo interpreter achieves the goals of providing immediate program execution, superior debugging capabilities, and educational value while maintaining the performance and reliability required for practical use.
RUNTIME VALUE SYSTEM IMPLEMENTATION
The runtime value system forms the foundation of the PyGo interpreter, providing comprehensive representation of all language data types during program execution. This system must handle type information, automatic conversions, and operations while maintaining efficiency and clarity.
java
import java.util.*;
// Base class for all runtime values
public abstract class PyGoValue {
protected PyGoType type;
public PyGoValue(PyGoType type) {
this.type = type;
}
public PyGoType getType() { return type; }
public abstract Object getValue();
public abstract String toString();
public abstract PyGoValue copy();
// Type checking methods
public boolean isInt() { return type instanceof IntType; }
public boolean isFloat() { return type instanceof FloatType; }
public boolean isString() { return type instanceof StringType; }
public boolean isBool() { return type instanceof BoolType; }
public boolean isFunction() { return type instanceof FunctionType; }
// Conversion methods with proper error handling
public int asInt() {
if (this instanceof IntValue) {
return ((IntValue) this).getValue();
} else if (this instanceof FloatValue) {
return (int) ((FloatValue) this).getValue();
} else if (this instanceof BoolValue) {
return ((BoolValue) this).getValue() ? 1 : 0;
}
throw new InterpreterException("Cannot convert " + type.getName() + " to int");
}
public double asFloat() {
if (this instanceof FloatValue) {
return ((FloatValue) this).getValue();
} else if (this instanceof IntValue) {
return (double) ((IntValue) this).getValue();
}
throw new InterpreterException("Cannot convert " + type.getName() + " to float");
}
public String asString() {
if (this instanceof StringValue) {
return ((StringValue) this).getValue();
}
return toString();
}
public boolean asBool() {
if (this instanceof BoolValue) {
return ((BoolValue) this).getValue();
} else if (this instanceof IntValue) {
return ((IntValue) this).getValue() != 0;
} else if (this instanceof FloatValue) {
return ((FloatValue) this).getValue() != 0.0;
} else if (this instanceof StringValue) {
return !((StringValue) this).getValue().isEmpty();
}
return true; // Non-null objects are truthy
}
}
// Integer value implementation
class IntValue extends PyGoValue {
private int value;
public IntValue(int value) {
super(new IntType());
this.value = value;
}
@Override
public Integer getValue() { return value; }
@Override
public String toString() { return String.valueOf(value); }
@Override
public PyGoValue copy() { return new IntValue(value); }
public void setValue(int value) { this.value = value; }
// Arithmetic operations
public IntValue add(IntValue other) {
return new IntValue(this.value + other.value);
}
public IntValue subtract(IntValue other) {
return new IntValue(this.value - other.value);
}
public IntValue multiply(IntValue other) {
return new IntValue(this.value * other.value);
}
public IntValue divide(IntValue other) {
if (other.value == 0) {
throw new InterpreterException("Division by zero");
}
return new IntValue(this.value / other.value);
}
public IntValue modulo(IntValue other) {
if (other.value == 0) {
throw new InterpreterException("Modulo by zero");
}
return new IntValue(this.value % other.value);
}
// Comparison operations
public BoolValue equals(IntValue other) {
return new BoolValue(this.value == other.value);
}
public BoolValue lessThan(IntValue other) {
return new BoolValue(this.value < other.value);
}
public BoolValue lessThanOrEqual(IntValue other) {
return new BoolValue(this.value <= other.value);
}
public BoolValue greaterThan(IntValue other) {
return new BoolValue(this.value > other.value);
}
public BoolValue greaterThanOrEqual(IntValue other) {
return new BoolValue(this.value >= other.value);
}
}
// Float value implementation
class FloatValue extends PyGoValue {
private double value;
public FloatValue(double value) {
super(new FloatType());
this.value = value;
}
@Override
public Double getValue() { return value; }
@Override
public String toString() {
// Format to avoid unnecessary decimal places
if (value == (long) value) {
return String.format("%.1f", value);
}
return String.valueOf(value);
}
@Override
public PyGoValue copy() { return new FloatValue(value); }
public void setValue(double value) { this.value = value; }
// Arithmetic operations with proper floating-point handling
public FloatValue add(FloatValue other) {
return new FloatValue(this.value + other.value);
}
public FloatValue subtract(FloatValue other) {
return new FloatValue(this.value - other.value);
}
public FloatValue multiply(FloatValue other) {
return new FloatValue(this.value * other.value);
}
public FloatValue divide(FloatValue other) {
if (other.value == 0.0) {
throw new InterpreterException("Division by zero");
}
return new FloatValue(this.value / other.value);
}
// Comparison operations with floating-point epsilon
private static final double EPSILON = 1e-9;
public BoolValue equals(FloatValue other) {
return new BoolValue(Math.abs(this.value - other.value) < EPSILON);
}
public BoolValue lessThan(FloatValue other) {
return new BoolValue(this.value < other.value - EPSILON);
}
public BoolValue lessThanOrEqual(FloatValue other) {
return new BoolValue(this.value <= other.value + EPSILON);
}
public BoolValue greaterThan(FloatValue other) {
return new BoolValue(this.value > other.value + EPSILON);
}
public BoolValue greaterThanOrEqual(FloatValue other) {
return new BoolValue(this.value >= other.value - EPSILON);
}
}
// String value implementation
class StringValue extends PyGoValue {
private String value;
public StringValue(String value) {
super(new StringType());
this.value = value != null ? value : "";
}
@Override
public String getValue() { return value; }
@Override
public String toString() { return value; }
@Override
public PyGoValue copy() { return new StringValue(value); }
public void setValue(String value) {
this.value = value != null ? value : "";
}
// String operations
public StringValue concatenate(StringValue other) {
return new StringValue(this.value + other.value);
}
public IntValue length() {
return new IntValue(this.value.length());
}
public BoolValue equals(StringValue other) {
return new BoolValue(this.value.equals(other.value));
}
public BoolValue contains(StringValue other) {
return new BoolValue(this.value.contains(other.value));
}
}
// Boolean value implementation
class BoolValue extends PyGoValue {
private boolean value;
public BoolValue(boolean value) {
super(new BoolType());
this.value = value;
}
@Override
public Boolean getValue() { return value; }
@Override
public String toString() { return String.valueOf(value); }
@Override
public PyGoValue copy() { return new BoolValue(value); }
public void setValue(boolean value) { this.value = value; }
// Logical operations
public BoolValue and(BoolValue other) {
return new BoolValue(this.value && other.value);
}
public BoolValue or(BoolValue other) {
return new BoolValue(this.value || other.value);
}
public BoolValue not() {
return new BoolValue(!this.value);
}
public BoolValue equals(BoolValue other) {
return new BoolValue(this.value == other.value);
}
}
// User-defined function value
class FunctionValue extends PyGoValue {
private FunctionDeclarationNode declaration;
private Environment closure;
public FunctionValue(FunctionDeclarationNode declaration, Environment closure) {
super(createFunctionType(declaration));
this.declaration = declaration;
this.closure = closure;
}
private static FunctionType createFunctionType(FunctionDeclarationNode declaration) {
List<PyGoType> paramTypes = new ArrayList<>();
for (ParameterNode param : declaration.getParameters()) {
paramTypes.add(convertTypeNode(param.getType()));
}
PyGoType returnType = declaration.getReturnType() != null ?
convertTypeNode(declaration.getReturnType()) : new VoidType();
return new FunctionType(returnType, paramTypes);
}
private static PyGoType convertTypeNode(TypeNode typeNode) {
if (typeNode == null) return new VoidType();
switch (typeNode.getTypeName()) {
case "int": return new IntType();
case "float": return new FloatType();
case "string": return new StringType();
case "bool": return new BoolType();
default: return new VoidType();
}
}
@Override
public FunctionDeclarationNode getValue() { return declaration; }
public Environment getClosure() { return closure; }
public String getName() { return declaration.getIdentifier(); }
public List<ParameterNode> getParameters() { return declaration.getParameters(); }
public BlockNode getBody() { return declaration.getBody(); }
public PyGoType getReturnType() {
return ((FunctionType) type).getReturnType();
}
@Override
public String toString() {
return "<function " + declaration.getIdentifier() + ">";
}
@Override
public PyGoValue copy() {
return new FunctionValue(declaration, closure);
}
}
// Built-in function value
class BuiltinFunctionValue extends PyGoValue {
private String name;
private BuiltinFunction function;
public interface BuiltinFunction {
PyGoValue call(List<PyGoValue> arguments, PyGoInterpreter interpreter) throws InterpreterException;
}
public BuiltinFunctionValue(String name, BuiltinFunction function, FunctionType type) {
super(type);
this.name = name;
this.function = function;
}
@Override
public BuiltinFunction getValue() { return function; }
public String getName() { return name; }
@Override
public String toString() { return "<builtin function " + name + ">"; }
@Override
public PyGoValue copy() {
return new BuiltinFunctionValue(name, function, (FunctionType) type);
}
public PyGoValue call(List<PyGoValue> arguments, PyGoInterpreter interpreter) throws InterpreterException {
return function.call(arguments, interpreter);
}
}
// Void value for functions that don't return anything
class VoidValue extends PyGoValue {
private static final VoidValue INSTANCE = new VoidValue();
private VoidValue() {
super(new VoidType());
}
public static VoidValue getInstance() {
return INSTANCE;
}
@Override
public Object getValue() { return null; }
@Override
public String toString() { return "void"; }
@Override
public PyGoValue copy() { return INSTANCE; }
}
ENVIRONMENT MANAGEMENT SYSTEM
java
import java.util.*;
// Runtime environment for variable and function bindings
public class Environment {
private Map<String, PyGoValue> bindings;
private Environment parent;
private String scopeName;
public Environment(Environment parent, String scopeName) {
this.bindings = new HashMap<>();
this.parent = parent;
this.scopeName = scopeName != null ? scopeName : "anonymous";
}
public Environment() {
this(null, "global");
}
// Variable binding operations
public void define(String name, PyGoValue value) {
if (bindings.containsKey(name)) {
throw new InterpreterException("Variable '" + name + "' already defined in current scope");
}
bindings.put(name, value);
}
public void assign(String name, PyGoValue value) {
if (bindings.containsKey(name)) {
bindings.put(name, value);
return;
}
if (parent != null) {
parent.assign(name, value);
return;
}
throw new InterpreterException("Undefined variable '" + name + "'");
}
public PyGoValue get(String name) {
if (bindings.containsKey(name)) {
return bindings.get(name);
}
if (parent != null) {
return parent.get(name);
}
throw new InterpreterException("Undefined variable '" + name + "'");
}
public boolean isDefined(String name) {
if (bindings.containsKey(name)) {
return true;
}
if (parent != null) {
return parent.isDefined(name);
}
return false;
}
public boolean isDefinedInCurrentScope(String name) {
return bindings.containsKey(name);
}
// Scope management
public Environment createChildScope(String name) {
return new Environment(this, name);
}
public Environment getParent() {
return parent;
}
public String getScopeName() {
return scopeName;
}
// Debugging and inspection
public Set<String> getLocalNames() {
return new HashSet<>(bindings.keySet());
}
public Map<String, PyGoValue> getAllBindings() {
Map<String, PyGoValue> allBindings = new HashMap<>();
if (parent != null) {
allBindings.putAll(parent.getAllBindings());
}
allBindings.putAll(bindings);
return allBindings;
}
public void printScope() {
System.out.println("Scope: " + scopeName);
for (Map.Entry<String, PyGoValue> entry : bindings.entrySet()) {
System.out.println(" " + entry.getKey() + " = " + entry.getValue());
}
if (parent != null) {
System.out.println("Parent scope:");
parent.printScope();
}
}
@Override
public String toString() {
return "Environment{scope=" + scopeName + ", bindings=" + bindings.keySet() + "}";
}
}
```
EXCEPTION HANDLING SYSTEM
java
// Base exception for interpreter errors
public class InterpreterException extends RuntimeException {
private int line;
private int column;
private String context;
public InterpreterException(String message) {
super(message);
this.line = -1;
this.column = -1;
this.context = "";
}
public InterpreterException(String message, int line, int column) {
super(message);
this.line = line;
this.column = column;
this.context = "";
}
public InterpreterException(String message, ASTNode node) {
super(message);
this.line = node.getLineNumber();
this.column = node.getColumnNumber();
this.context = "";
}
public InterpreterException(String message, String context) {
super(message);
this.line = -1;
this.column = -1;
this.context = context;
}
public int getLine() { return line; }
public int getColumn() { return column; }
public String getContext() { return context; }
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Runtime error");
if (line >= 0) {
sb.append(" at line ").append(line);
if (column >= 0) {
sb.append(", column ").append(column);
}
}
sb.append(": ").append(getMessage());
if (!context.isEmpty()) {
sb.append(" (in ").append(context).append(")");
}
return sb.toString();
}
}
// Type error exception
class TypeException extends InterpreterException {
public TypeException(String message) {
super("Type error: " + message);
}
public TypeException(String message, ASTNode node) {
super("Type error: " + message, node);
}
}
// Control flow exceptions for return, break, continue
class ReturnException extends InterpreterException {
private PyGoValue returnValue;
public ReturnException(PyGoValue returnValue) {
super("Return statement");
this.returnValue = returnValue;
}
public PyGoValue getReturnValue() {
return returnValue;
}
}
class BreakException extends InterpreterException {
public BreakException() {
super("Break statement");
}
}
class ContinueException extends InterpreterException {
public ContinueException() {
super("Continue statement");
}
}
// Division by zero exception
class DivisionByZeroException extends InterpreterException {
public DivisionByZeroException(ASTNode node) {
super("Division by zero", node);
}
}
// Function call exception
class FunctionCallException extends InterpreterException {
public FunctionCallException(String message, String functionName) {
super(message, "function " + functionName);
}
}
```
CORE INTERPRETER IMPLEMENTATION
java
import java.util.*;
import java.io.*;
public class PyGoInterpreter implements ASTVisitor {
private Environment globalEnvironment;
private Environment currentEnvironment;
private PrintStream outputStream;
private boolean debugMode;
private int callDepth;
private static final int MAX_CALL_DEPTH = 1000;
public PyGoInterpreter() {
this.globalEnvironment = new Environment();
this.currentEnvironment = globalEnvironment;
this.outputStream = System.out;
this.debugMode = false;
this.callDepth = 0;
setupBuiltinFunctions();
}
public PyGoInterpreter(PrintStream outputStream) {
this();
this.outputStream = outputStream;
}
public void setDebugMode(boolean debug) {
this.debugMode = debug;
}
public void setOutputStream(PrintStream stream) {
this.outputStream = stream;
}
// Setup built-in functions
private void setupBuiltinFunctions() {
// Print function
FunctionType printType = new FunctionType(new VoidType(),
Arrays.asList(new StringType()));
BuiltinFunctionValue printFunc = new BuiltinFunctionValue("print",
(args, interpreter) -> {
if (args.size() != 1) {
throw new FunctionCallException("print() takes exactly 1 argument", "print");
}
interpreter.outputStream.println(args.get(0).toString());
return VoidValue.getInstance();
}, printType);
globalEnvironment.define("print", printFunc);
// Type conversion functions
FunctionType intType = new FunctionType(new IntType(),
Arrays.asList(new StringType()));
BuiltinFunctionValue intFunc = new BuiltinFunctionValue("int",
(args, interpreter) -> {
if (args.size() != 1) {
throw new FunctionCallException("int() takes exactly 1 argument", "int");
}
PyGoValue arg = args.get(0);
if (arg instanceof StringValue) {
try {
int value = Integer.parseInt(((StringValue) arg).getValue());
return new IntValue(value);
} catch (NumberFormatException e) {
throw new InterpreterException("Cannot convert '" + arg + "' to int");
}
} else if (arg instanceof FloatValue) {
return new IntValue((int) ((FloatValue) arg).getValue());
} else if (arg instanceof IntValue) {
return arg;
} else if (arg instanceof BoolValue) {
return new IntValue(((BoolValue) arg).getValue() ? 1 : 0);
}
throw new InterpreterException("Cannot convert " + arg.getType().getName() + " to int");
}, intType);
globalEnvironment.define("int", intFunc);
// Float conversion function
FunctionType floatType = new FunctionType(new FloatType(),
Arrays.asList(new StringType()));
BuiltinFunctionValue floatFunc = new BuiltinFunctionValue("float",
(args, interpreter) -> {
if (args.size() != 1) {
throw new FunctionCallException("float() takes exactly 1 argument", "float");
}
PyGoValue arg = args.get(0);
if (arg instanceof StringValue) {
try {
double value = Double.parseDouble(((StringValue) arg).getValue());
return new FloatValue(value);
} catch (NumberFormatException e) {
throw new InterpreterException("Cannot convert '" + arg + "' to float");
}
} else if (arg instanceof IntValue) {
return new FloatValue((double) ((IntValue) arg).getValue());
} else if (arg instanceof FloatValue) {
return arg;
}
throw new InterpreterException("Cannot convert " + arg.getType().getName() + " to float");
}, floatType);
globalEnvironment.define("float", floatFunc);
// String conversion function
FunctionType stringType = new FunctionType(new StringType(),
Arrays.asList(new IntType()));
BuiltinFunctionValue stringFunc = new BuiltinFunctionValue("string",
(args, interpreter) -> {
if (args.size() != 1) {
throw new FunctionCallException("string() takes exactly 1 argument", "string");
}
return new StringValue(args.get(0).toString());
}, stringType);
globalEnvironment.define("string", stringFunc);
}
// Main interpretation entry point
public PyGoValue interpret(ProgramNode program) {
try {
if (debugMode) {
outputStream.println("Starting PyGo program interpretation...");
}
program.accept(this);
// Look for main function and execute it
if (globalEnvironment.isDefined("main")) {
PyGoValue mainFunc = globalEnvironment.get("main");
if (mainFunc instanceof FunctionValue) {
return callFunction((FunctionValue) mainFunc, new ArrayList<>());
} else {
throw new InterpreterException("'main' is not a function");
}
} else {
if (debugMode) {
outputStream.println("No main function found, program completed.");
}
return VoidValue.getInstance();
}
} catch (ReturnException e) {
return e.getReturnValue();
} catch (InterpreterException e) {
if (debugMode) {
outputStream.println("Interpreter error: " + e.getMessage());
globalEnvironment.printScope();
}
throw e;
}
}
@Override
public void visitProgram(ProgramNode node) {
if (debugMode) {
outputStream.println("Processing " + node.getDeclarations().size() + " declarations...");
}
for (DeclarationNode declaration : node.getDeclarations()) {
declaration.accept(this);
}
}
@Override
public void visitFunctionDeclaration(FunctionDeclarationNode node) {
String functionName = node.getIdentifier();
if (debugMode) {
outputStream.println("Defining function: " + functionName);
}
if (currentEnvironment.isDefinedInCurrentScope(functionName)) {
throw new InterpreterException("Function '" + functionName + "' already defined", node);
}
// Create function value with current environment as closure
FunctionValue functionValue = new FunctionValue(node, currentEnvironment);
currentEnvironment.define(functionName, functionValue);
}
@Override
public void visitVariableDeclaration(VariableDeclarationNode node) {
String varName = node.getIdentifier();
if (debugMode) {
outputStream.println("Declaring variable: " + varName);
}
if (currentEnvironment.isDefinedInCurrentScope(varName)) {
throw new InterpreterException("Variable '" + varName + "' already defined", node);
}
PyGoValue value;
if (node.getInitializer() != null) {
value = evaluateExpression(node.getInitializer());
} else {
// Initialize with default value based on type
value = getDefaultValue(node.getType());
}
currentEnvironment.define(varName, value);
}
private PyGoValue getDefaultValue(TypeNode typeNode) {
switch (typeNode.getTypeName()) {
case "int":
return new IntValue(0);
case "float":
return new FloatValue(0.0);
case "string":
return new StringValue("");
case "bool":
return new BoolValue(false);
default:
return VoidValue.getInstance();
}
}
@Override
public void visitParameter(ParameterNode node) {
// Parameters are handled in function calls
}
@Override
public void visitType(TypeNode node) {
// Types are handled where they're used
}
@Override
public void visitBlock(BlockNode node) {
// Create new scope for the block
Environment previousEnvironment = currentEnvironment;
currentEnvironment = currentEnvironment.createChildScope("block");
try {
for (StatementNode statement : node.getStatements()) {
statement.accept(this);
}
} finally {
// Restore previous environment
currentEnvironment = previousEnvironment;
}
}
@Override
public void visitAssignmentStatement(AssignmentStatementNode node) {
String varName = node.getIdentifier();
PyGoValue value = evaluateExpression(node.getExpression());
if (debugMode) {
outputStream.println("Assigning " + value + " to " + varName);
}
if (!currentEnvironment.isDefined(varName)) {
throw new InterpreterException("Undefined variable '" + varName + "'", node);
}
currentEnvironment.assign(varName, value);
}
@Override
public void visitIfStatement(IfStatementNode node) {
PyGoValue conditionValue = evaluateExpression(node.getCondition());
if (debugMode) {
outputStream.println("If condition: " + conditionValue);
}
if (conditionValue.asBool()) {
node.getThenBlock().accept(this);
} else if (node.getElseBlock() != null) {
node.getElseBlock().accept(this);
}
}
@Override
public void visitWhileStatement(WhileStatementNode node) {
if (debugMode) {
outputStream.println("Entering while loop");
}
try {
while (true) {
PyGoValue conditionValue = evaluateExpression(node.getCondition());
if (!conditionValue.asBool()) {
break;
}
try {
node.getBody().accept(this);
} catch (BreakException e) {
break;
} catch (ContinueException e) {
continue;
}
}
} catch (BreakException e) {
// Break from outer loop
}
if (debugMode) {
outputStream.println("Exiting while loop");
}
}
@Override
public void visitForStatement(ForStatementNode node) {
if (debugMode) {
outputStream.println("Entering for loop");
}
// Create new scope for the for loop
Environment previousEnvironment = currentEnvironment;
currentEnvironment = currentEnvironment.createChildScope("for");
try {
String iterator = node.getIterator();
PyGoValue initialValue = evaluateExpression(node.getInitialValue());
// Define iterator variable
currentEnvironment.define(iterator, initialValue);
while (true) {
PyGoValue conditionValue = evaluateExpression(node.getCondition());
if (!conditionValue.asBool()) {
break;
}
try {
node.getBody().accept(this);
} catch (BreakException e) {
break;
} catch (ContinueException e) {
// Continue to increment
}
// Execute increment
node.getIncrement().accept(this);
}
} finally {
currentEnvironment = previousEnvironment;
}
if (debugMode) {
outputStream.println("Exiting for loop");
}
}
@Override
public void visitReturnStatement(ReturnStatementNode node) {
PyGoValue returnValue;
if (node.getExpression() != null) {
returnValue = evaluateExpression(node.getExpression());
} else {
returnValue = VoidValue.getInstance();
}
if (debugMode) {
outputStream.println("Returning: " + returnValue);
}
throw new ReturnException(returnValue);
}
@Override
public void visitExpressionStatement(ExpressionStatementNode node) {
evaluateExpression(node.getExpression());
}
// Expression evaluation methods
@Override
public void visitBinaryExpression(BinaryExpressionNode node) {
// Handled by evaluateExpression
}
@Override
public void visitUnaryExpression(UnaryExpressionNode node) {
// Handled by evaluateExpression
}
@Override
public void visitLiteralExpression(LiteralExpressionNode node) {
// Handled by evaluateExpression
}
@Override
public void visitIdentifierExpression(IdentifierExpressionNode node) {
// Handled by evaluateExpression
}
@Override
public void visitFunctionCallExpression(FunctionCallExpressionNode node) {
// Handled by evaluateExpression
}
// Main expression evaluation method
private PyGoValue evaluateExpression(ExpressionNode node) {
if (node instanceof BinaryExpressionNode) {
return evaluateBinaryExpression((BinaryExpressionNode) node);
} else if (node instanceof UnaryExpressionNode) {
return evaluateUnaryExpression((UnaryExpressionNode) node);
} else if (node instanceof LiteralExpressionNode) {
return evaluateLiteralExpression((LiteralExpressionNode) node);
} else if (node instanceof IdentifierExpressionNode) {
return evaluateIdentifierExpression((IdentifierExpressionNode) node);
} else if (node instanceof FunctionCallExpressionNode) {
return evaluateFunctionCallExpression((FunctionCallExpressionNode) node);
}
throw new InterpreterException("Unknown expression type: " + node.getClass().getSimpleName(), node);
}
private PyGoValue evaluateBinaryExpression(BinaryExpressionNode node) {
String operator = node.getOperator();
// Handle short-circuit evaluation for logical operators
if (operator.equals("and")) {
PyGoValue left = evaluateExpression(node.getLeft());
if (!left.asBool()) {
return new BoolValue(false);
}
PyGoValue right = evaluateExpression(node.getRight());
return new BoolValue(right.asBool());
} else if (operator.equals("or")) {
PyGoValue left = evaluateExpression(node.getLeft());
if (left.asBool()) {
return new BoolValue(true);
}
PyGoValue right = evaluateExpression(node.getRight());
return new BoolValue(right.asBool());
}
// Evaluate both operands for other operators
PyGoValue left = evaluateExpression(node.getLeft());
PyGoValue right = evaluateExpression(node.getRight());
return performBinaryOperation(left, operator, right, node);
}
private PyGoValue performBinaryOperation(PyGoValue left, String operator, PyGoValue right, ASTNode node) {
// Arithmetic operations
if (operator.equals("+")) {
if (left.isInt() && right.isInt()) {
return new IntValue(left.asInt() + right.asInt());
} else if ((left.isInt() || left.isFloat()) && (right.isInt() || right.isFloat())) {
return new FloatValue(left.asFloat() + right.asFloat());
} else if (left.isString() && right.isString()) {
return new StringValue(left.asString() + right.asString());
}
throw new TypeException("Cannot add " + left.getType().getName() + " and " + right.getType().getName(), node);
}
if (operator.equals("-")) {
if (left.isInt() && right.isInt()) {
return new IntValue(left.asInt() - right.asInt());
} else if ((left.isInt() || left.isFloat()) && (right.isInt() || right.isFloat())) {
return new FloatValue(left.asFloat() - right.asFloat());
}
throw new TypeException("Cannot subtract " + right.getType().getName() + " from " + left.getType().getName(), node);
}
if (operator.equals("*")) {
if (left.isInt() && right.isInt()) {
return new IntValue(left.asInt() * right.asInt());
} else if ((left.isInt() || left.isFloat()) && (right.isInt() || right.isFloat())) {
return new FloatValue(left.asFloat() * right.asFloat());
}
throw new TypeException("Cannot multiply " + left.getType().getName() + " and " + right.getType().getName(), node);
}
if (operator.equals("/")) {
if (left.isInt() && right.isInt()) {
int rightVal = right.asInt();
if (rightVal == 0) {
throw new DivisionByZeroException(node);
}
return new IntValue(left.asInt() / rightVal);
} else if ((left.isInt() || left.isFloat()) && (right.isInt() || right.isFloat())) {
double rightVal = right.asFloat();
if (rightVal == 0.0) {
throw new DivisionByZeroException(node);
}
return new FloatValue(left.asFloat() / rightVal);
}
throw new TypeException("Cannot divide " + left.getType().getName() + " by " + right.getType().getName(), node);
}
if (operator.equals("%")) {
if (left.isInt() && right.isInt()) {
int rightVal = right.asInt();
if (rightVal == 0) {
throw new DivisionByZeroException(node);
}
return new IntValue(left.asInt() % rightVal);
}
throw new TypeException("Modulo operator requires integer operands", node);
}
// Comparison operations
if (operator.equals("==")) {
return new BoolValue(compareValues(left, right) == 0);
}
if (operator.equals("!=")) {
return new BoolValue(compareValues(left, right) != 0);
}
if (operator.equals("<")) {
return new BoolValue(compareValues(left, right) < 0);
}
if (operator.equals("<=")) {
return new BoolValue(compareValues(left, right) <= 0);
}
if (operator.equals(">")) {
return new BoolValue(compareValues(left, right) > 0);
}
if (operator.equals(">=")) {
return new BoolValue(compareValues(left, right) >= 0);
}
throw new InterpreterException("Unknown binary operator: " + operator, node);
}
private int compareValues(PyGoValue left, PyGoValue right) {
if (left.isInt() && right.isInt()) {
return Integer.compare(left.asInt(), right.asInt());
} else if ((left.isInt() || left.isFloat()) && (right.isInt() || right.isFloat())) {
return Double.compare(left.asFloat(), right.asFloat());
} else if (left.isString() && right.isString()) {
return left.asString().compareTo(right.asString());
} else if (left.isBool() && right.isBool()) {
return Boolean.compare(left.asBool(), right.asBool());
} else {
throw new TypeException("Cannot compare " + left.getType().getName() + " and " + right.getType().getName());
}
}
private PyGoValue evaluateUnaryExpression(UnaryExpressionNode node) {
String operator = node.getOperator();
PyGoValue operand = evaluateExpression(node.getOperand());
if (operator.equals("-")) {
if (operand.isInt()) {
return new IntValue(-operand.asInt());
} else if (operand.isFloat()) {
return new FloatValue(-operand.asFloat());
}
throw new TypeException("Cannot negate " + operand.getType().getName(), node);
}
if (operator.equals("not")) {
return new BoolValue(!operand.asBool());
}
throw new InterpreterException("Unknown unary operator: " + operator, node);
}
private PyGoValue evaluateLiteralExpression(LiteralExpressionNode node) {
Object value = node.getValue();
String literalType = node.getLiteralType();
switch (literalType) {
case "int":
return new IntValue((Integer) value);
case "float":
return new FloatValue((Double) value);
case "string":
return new StringValue((String) value);
case "bool":
return new BoolValue((Boolean) value);
default:
throw new InterpreterException("Unknown literal type: " + literalType, node);
}
}
private PyGoValue evaluateIdentifierExpression(IdentifierExpressionNode node) {
String identifier = node.getIdentifier();
if (!currentEnvironment.isDefined(identifier)) {
throw new InterpreterException("Undefined variable '" + identifier + "'", node);
}
return currentEnvironment.get(identifier);
}
private PyGoValue evaluateFunctionCallExpression(FunctionCallExpressionNode node) {
String functionName = node.getFunctionName();
if (!currentEnvironment.isDefined(functionName)) {
throw new InterpreterException("Undefined function '" + functionName + "'", node);
}
PyGoValue functionValue = currentEnvironment.get(functionName);
if (!(functionValue instanceof FunctionValue) && !(functionValue instanceof BuiltinFunctionValue)) {
throw new InterpreterException("'" + functionName + "' is not a function", node);
}
// Evaluate arguments
List<PyGoValue> arguments = new ArrayList<>();
for (ExpressionNode argExpr : node.getArguments()) {
arguments.add(evaluateExpression(argExpr));
}
if (functionValue instanceof BuiltinFunctionValue) {
return ((BuiltinFunctionValue) functionValue).call(arguments, this);
} else {
return callFunction((FunctionValue) functionValue, arguments);
}
}
private PyGoValue callFunction(FunctionValue function, List<PyGoValue> arguments) {
if (callDepth >= MAX_CALL_DEPTH) {
throw new InterpreterException("Maximum call depth exceeded (possible infinite recursion)");
}
FunctionDeclarationNode declaration = function.getValue();
List<ParameterNode> parameters = declaration.getParameters();
// Check argument count
if (arguments.size() != parameters.size()) {
throw new FunctionCallException(
"Function '" + declaration.getIdentifier() + "' expects " +
parameters.size() + " arguments, got " + arguments.size(),
declaration.getIdentifier()
);
}
if (debugMode) {
outputStream.println("Calling function: " + declaration.getIdentifier() +
" with " + arguments.size() + " arguments");
}
// Create new environment for function execution
Environment previousEnvironment = currentEnvironment;
currentEnvironment = function.getClosure().createChildScope("function:" + declaration.getIdentifier());
try {
callDepth++;
// Bind parameters to arguments
for (int i = 0; i < parameters.size(); i++) {
String paramName = parameters.get(i).getIdentifier();
PyGoValue argValue = arguments.get(i);
currentEnvironment.define(paramName, argValue);
if (debugMode) {
outputStream.println(" Parameter " + paramName + " = " + argValue);
}
}
// Execute function body
declaration.getBody().accept(this);
// If no explicit return, return void or default value
PyGoType returnType = function.getReturnType();
if (returnType instanceof VoidType) {
return VoidValue.getInstance();
} else {
// Return default value for the return type
if (returnType instanceof IntType) {
return new IntValue(0);
} else if (returnType instanceof FloatType) {
return new FloatValue(0.0);
} else if (returnType instanceof StringType) {
return new StringValue("");
} else if (returnType instanceof BoolType) {
return new BoolValue(false);
}
return VoidValue.getInstance();
}
} catch (ReturnException e) {
if (debugMode) {
outputStream.println("Function " + declaration.getIdentifier() + " returned: " + e.getReturnValue());
}
return e.getReturnValue();
} finally {
callDepth--;
currentEnvironment = previousEnvironment;
}
}
// Utility methods for debugging and inspection
public Environment getCurrentEnvironment() {
return currentEnvironment;
}
public Environment getGlobalEnvironment() {
return globalEnvironment;
}
public void printEnvironment() {
currentEnvironment.printScope();
}
public int getCallDepth() {
return callDepth;
}
}
INTERPRETER INTEGRATION AND TESTING FRAMEWORK
java
import java.io.*;
import java.util.*;
import java.nio.file.*;
public class PyGoInterpreterWrapper {
private PyGoCompiler compiler;
private PyGoInterpreter interpreter;
private boolean debugMode;
public PyGoInterpreterWrapper() {
this.compiler = new PyGoCompiler();
this.interpreter = new PyGoInterpreter();
this.debugMode = false;
}
public void setDebugMode(boolean debug) {
this.debugMode = debug;
interpreter.setDebugMode(debug);
}
public void setOutputStream(PrintStream stream) {
interpreter.setOutputStream(stream);
}
public InterpretationResult interpret(String sourceCode) {
long startTime = System.currentTimeMillis();
try {
if (debugMode) {
System.out.println("=== PyGo Interpretation Started ===");
}
// Parse the source code
PyGoCompiler.CompilationResult compileResult = compiler.compile(sourceCode);
if (compileResult.hasErrors()) {
return new InterpretationResult(null, compileResult.getErrors(),
System.currentTimeMillis() - startTime);
}
// Interpret the AST
PyGoValue result = interpreter.interpret(compileResult.getAST());
if (debugMode) {
System.out.println("=== Interpretation Completed Successfully ===");
System.out.println("Result: " + result);
System.out.println("Total time: " + (System.currentTimeMillis() - startTime) + "ms");
}
return new InterpretationResult(result, new ArrayList<>(),
System.currentTimeMillis() - startTime);
} catch (InterpreterException e) {
List<CompilerError> errors = Arrays.asList(
new RuntimeError(e.getLine(), e.getColumn(), e.getMessage())
);
return new InterpretationResult(null, errors, System.currentTimeMillis() - startTime);
} catch (Exception e) {
List<CompilerError> errors = Arrays.asList(
new RuntimeError(0, 0, "Internal interpreter error: " + e.getMessage())
);
return new InterpretationResult(null, errors, System.currentTimeMillis() - startTime);
}
}
public void interpretFile(String filename) {
try {
String sourceCode = Files.readString(Paths.get(filename));
InterpretationResult result = interpret(sourceCode);
if (result.isSuccessful()) {
if (debugMode) {
System.out.println("Program executed successfully.");
System.out.println("Final result: " + result.getResult());
}
} else {
System.err.println("Interpretation failed with " + result.getErrors().size() + " errors:");
for (CompilerError error : result.getErrors()) {
System.err.println(" " + error);
}
}
} catch (IOException e) {
System.err.println("Error reading file: " + e.getMessage());
}
}
// Interactive REPL mode
public void startREPL() {
Scanner scanner = new Scanner(System.in);
System.out.println("PyGo Interactive Interpreter");
System.out.println("Type 'exit' to quit, 'debug' to toggle debug mode");
System.out.println("Enter PyGo expressions or statements:");
while (true) {
System.out.print(">>> ");
String input = scanner.nextLine().trim();
if (input.equals("exit")) {
break;
} else if (input.equals("debug")) {
debugMode = !debugMode;
setDebugMode(debugMode);
System.out.println("Debug mode: " + (debugMode ? "ON" : "OFF"));
continue;
} else if (input.equals("env")) {
interpreter.printEnvironment();
continue;
} else if (input.isEmpty()) {
continue;
}
// Wrap single expressions in a main function for execution
String wrappedCode;
if (input.contains("func") || input.contains("var")) {
wrappedCode = input + "\nfunc main(): {}";
} else {
wrappedCode = "func main(): { " + input + " }";
}
InterpretationResult result = interpret(wrappedCode);
if (result.isSuccessful()) {
if (result.getResult() != null && !(result.getResult() instanceof VoidValue)) {
System.out.println("=> " + result.getResult());
}
} else {
for (CompilerError error : result.getErrors()) {
System.err.println("Error: " + error.getMessage());
}
}
}
scanner.close();
System.out.println("Goodbye!");
}
// Result class for interpretation
public static class InterpretationResult {
private PyGoValue result;
private List<CompilerError> errors;
private long interpretationTime;
public InterpretationResult(PyGoValue result, List<CompilerError> errors, long interpretationTime) {
this.result = result;
this.errors = new ArrayList<>(errors);
this.interpretationTime = interpretationTime;
}
public PyGoValue getResult() { return result; }
public List<CompilerError> getErrors() { return new ArrayList<>(errors); }
public long getInterpretationTime() { return interpretationTime; }
public boolean hasErrors() { return !errors.isEmpty(); }
public boolean isSuccessful() { return result != null && errors.isEmpty(); }
}
}
// Runtime error class
class RuntimeError extends CompilerError {
public RuntimeError(int line, int column, String message) {
super(line, column, message, "Runtime");
}
}
COMPREHENSIVE TESTING FRAMEWORK
java
public class PyGoInterpreterTest {
private PyGoInterpreterWrapper interpreter;
public PyGoInterpreterTest() {
this.interpreter = new PyGoInterpreterWrapper();
this.interpreter.setDebugMode(false); // Set to true for detailed output
}
public void runAllTests() {
System.out.println("Running PyGo Interpreter Tests");
System.out.println("==============================");
testBasicArithmetic();
testVariableOperations();
testControlFlow();
testFunctionCalls();
testRecursion();
testBuiltinFunctions();
testErrorHandling();
testComplexPrograms();
System.out.println("\nAll interpreter tests completed successfully!");
}
private void testBasicArithmetic() {
System.out.println("\n--- Testing Basic Arithmetic ---");
String program = """
func main():
{
var a: int = 10
var b: int = 3
var sum: int = a + b
var diff: int = a - b
var product: int = a * b
var quotient: int = a / b
var remainder: int = a % b
print(sum)
print(diff)
print(product)
print(quotient)
print(remainder)
}
""";
PyGoInterpreterWrapper.InterpretationResult result = interpreter.interpret(program);
assert result.isSuccessful() : "Basic arithmetic should work";
System.out.println("✓ Basic arithmetic operations working");
}
private void testVariableOperations() {
System.out.println("\n--- Testing Variable Operations ---");
String program = """
func main():
{
var x: int = 5
var y: float = 3.14
var name: string = "PyGo"
var flag: bool = true
print(x)
print(y)
print(name)
print(flag)
x = x + 1
y = y * 2.0
name = name + " Language"
flag = not flag
print(x)
print(y)
print(name)
print(flag)
}
""";
PyGoInterpreterWrapper.InterpretationResult result = interpreter.interpret(program);
assert result.isSuccessful() : "Variable operations should work";
System.out.println("✓ Variable operations working");
}
private void testControlFlow() {
System.out.println("\n--- Testing Control Flow ---");
String program = """
func main():
{
var i: int = 0
# Test if statement
if i == 0:
{
print("i is zero")
}
else:
{
print("i is not zero")
}
# Test while loop
while i < 3:
{
print(i)
i = i + 1
}
# Test for loop
for j = 0; j < 3; j = j + 1:
{
print(j * 10)
}
}
""";
PyGoInterpreterWrapper.InterpretationResult result = interpreter.interpret(program);
assert result.isSuccessful() : "Control flow should work";
System.out.println("✓ Control flow statements working");
}
private void testFunctionCalls() {
System.out.println("\n--- Testing Function Calls ---");
String program = """
func add(a: int, b: int) -> int:
{
return a + b
}
func greet(name: string):
{
print("Hello, " + name + "!")
}
func main():
{
var result: int = add(5, 3)
print(result)
greet("World")
greet("PyGo")
}
""";
PyGoInterpreterWrapper.InterpretationResult result = interpreter.interpret(program);
assert result.isSuccessful() : "Function calls should work";
System.out.println("✓ Function calls working");
}
private void testRecursion() {
System.out.println("\n--- Testing Recursion ---");
String program = """
func factorial(n: int) -> int:
{
if n <= 1:
{
return 1
}
else:
{
return n * factorial(n - 1)
}
}
func fibonacci(n: int) -> int:
{
if n <= 1:
{
return n
}
else:
{
return fibonacci(n - 1) + fibonacci(n - 2)
}
}
func main():
{
var fact5: int = factorial(5)
var fib7: int = fibonacci(7)
print(fact5) # Should print 120
print(fib7) # Should print 13
}
""";
PyGoInterpreterWrapper.InterpretationResult result = interpreter.interpret(program);
assert result.isSuccessful() : "Recursion should work";
System.out.println("✓ Recursive functions working");
}
private void testBuiltinFunctions() {
System.out.println("\n--- Testing Built-in Functions ---");
String program = """
func main():
{
print("Testing built-in functions")
var str_num: string = "42"
var int_val: int = int(str_num)
print(int_val)
var float_val: float = float("3.14")
print(float_val)
var str_val: string = string(123)
print(str_val)
}
""";
PyGoInterpreterWrapper.InterpretationResult result = interpreter.interpret(program);
assert result.isSuccessful() : "Built-in functions should work";
System.out.println("✓ Built-in functions working");
}
private void testErrorHandling() {
System.out.println("\n--- Testing Error Handling ---");
// Test division by zero
String divisionByZero = """
func main():
{
var x: int = 10
var y: int = 0
var result: int = x / y
}
""";
PyGoInterpreterWrapper.InterpretationResult result = interpreter.interpret(divisionByZero);
assert result.hasErrors() : "Division by zero should produce error";
System.out.println("✓ Division by zero error handling working");
// Test undefined variable
String undefinedVar = """
func main():
{
print(undefined_variable)
}
""";
result = interpreter.interpret(undefinedVar);
assert result.hasErrors() : "Undefined variable should produce error";
System.out.println("✓ Undefined variable error handling working");
}
private void testComplexPrograms() {
System.out.println("\n--- Testing Complex Programs ---");
String complexProgram = """
func isPrime(n: int) -> bool:
{
if n <= 1:
{
return false
}
var i: int = 2
while i * i <= n:
{
if n % i == 0:
{
return false
}
i = i + 1
}
return true
}
func printPrimes(limit: int):
{
print("Prime numbers up to " + string(limit) + ":")
for i = 2; i <= limit; i = i + 1:
{
if isPrime(i):
{
print(i)
}
}
}
func main():
{
printPrimes(20)
}
""";
PyGoInterpreterWrapper.InterpretationResult result = interpreter.interpret(complexProgram);
assert result.isSuccessful() : "Complex program should work";
System.out.println("✓ Complex program execution working");
}
public static void main(String[] args) {
PyGoInterpreterTest test = new PyGoInterpreterTest();
test.runAllTests();
// Optionally start REPL for interactive testing
if (args.length > 0 && args[0].equals("--repl")) {
PyGoInterpreterWrapper interpreter = new PyGoInterpreterWrapper();
interpreter.startREPL();
}
}
}
```
COMMAND-LINE INTERFACE
public class PyGoInterpreterMain {
public static void main(String[] args) {
if (args.length == 0) {
printUsage();
return;
}
PyGoInterpreterWrapper interpreter = new PyGoInterpreterWrapper();
boolean debugMode = false;
String filename = null;
boolean replMode = false;
// Parse command line arguments
for (int i = 0; i < args.length; i++) {
switch (args[i]) {
case "--debug":
case "-d":
debugMode = true;
break;
case "--repl":
case "-r":
replMode = true;
break;
case "--help":
case "-h":
printUsage();
return;
default:
if (filename == null && !args[i].startsWith("-")) {
filename = args[i];
}
break;
}
}
interpreter.setDebugMode(debugMode);
if (replMode) {
interpreter.startREPL();
} else if (filename != null) {
interpreter.interpretFile(filename);
} else {
System.err.println("Error: No input file specified");
printUsage();
}
}
private static void printUsage() {
System.out.println("PyGo Interpreter");
System.out.println("Usage: java PyGoInterpreterMain [options] <file.pygo>");
System.out.println();
System.out.println("Options:");
System.out.println(" -d, --debug Enable debug mode");
System.out.println(" -r, --repl Start interactive REPL");
System.out.println(" -h, --help Show this help message");
System.out.println();
System.out.println("Examples:");
System.out.println(" java PyGoInterpreterMain program.pygo");
System.out.println(" java PyGoInterpreterMain --debug program.pygo");
System.out.println(" java PyGoInterpreterMain --repl");
}
}
SAMPLE PROGRAMS FOR INTERPRETER TESTING
FILE: fibonacci_demo.pygo
pygo
func fibonacci(n: int) -> int:
{
if n <= 1:
{
return n
}
else:
{
return fibonacci(n - 1) + fibonacci(n - 2)
}
}
func main():
{
print("Fibonacci sequence:")
for i = 0; i < 15; i = i + 1:
{
var fib_num: int = fibonacci(i)
print("F(" + string(i) + ") = " + string(fib_num))
}
}
FILE: calculator_demo.pygo
pygo
func add(a: float, b: float) -> float:
{
return a + b
}
func subtract(a: float, b: float) -> float:
{
return a - b
}
func multiply(a: float, b: float) -> float:
{
return a * b
}
func divide(a: float, b: float) -> float:
{
if b == 0.0:
{
print("Error: Division by zero!")
return 0.0
}
return a / b
}
func power(base: float, exp: int) -> float:
{
if exp == 0:
{
return 1.0
}
var result: float = 1.0
var i: int = 0
while i < exp:
{
result = multiply(result, base)
i = i + 1
}
return result
}
func main():
{
var x: float = 10.5
var y: float = 3.2
print("Calculator Demo")
print("x = " + string(x))
print("y = " + string(y))
print("x + y = " + string(add(x, y)))
print("x - y = " + string(subtract(x, y)))
print("x * y = " + string(multiply(x, y)))
print("x / y = " + string(divide(x, y)))
print("x^3 = " + string(power(x, 3)))
}
CONCLUSION
This comprehensive PyGo interpreter implementation provides a complete alternative execution model for PyGo programs. The interpreter demonstrates modern language implementation techniques while maintaining clarity, extensibility, and educational value.
The runtime value system provides comprehensive type representation with automatic conversions and proper error handling. The environment management system implements lexical scoping with closure support and efficient variable lookup. The expression evaluation engine handles all PyGo operators with correct precedence and type checking.
The interpreter includes robust error handling that provides detailed runtime information including line numbers, context, and helpful error messages. The testing framework validates all language features and edge cases, ensuring reliable operation across diverse programs.
The command-line interface and REPL mode provide practical tools for PyGo development and interactive exploration. The interpreter serves both educational and practical purposes, offering insight into language implementation while providing a complete execution environment for PyGo programs.
The combination of compiler and interpreter gives PyGo developers flexible execution options optimized for different phases of software development, from initial prototyping through production deployment.
In the seventh and final part of this article series, you‘ll learn about Recursive Descent Parsing of PyGo.
No comments:
Post a Comment