petcarescript
Version:
PetCareScript - A modern, expressive programming language designed for humans
199 lines (164 loc) • 5.57 kB
JavaScript
/**
* PetCareScript Runtime Classes
* Runtime support classes for the interpreter
*/
class PCFunction {
constructor(declaration, closure, isInitializer = false) {
this.declaration = declaration;
this.closure = closure;
this.isInitializer = isInitializer;
// Handle different function types
if (declaration.name) {
this.name = declaration.name.lexeme || declaration.name;
} else {
this.name = '<anonymous>';
}
// Check if it's an arrow function
this.isArrowFunction = declaration.constructor.name === 'ArrowFunctionNode';
}
arity() {
return this.declaration.params ? this.declaration.params.length : 0;
}
call(interpreter, args) {
// Import Environment here to avoid circular dependencies
const Environment = require('../interpreter/environment');
// Create new environment for function execution
const environment = new Environment(this.closure);
// Define parameters in the function environment
const params = this.declaration.params || [];
for (let i = 0; i < params.length; i++) {
const paramName = params[i].lexeme || params[i].name?.lexeme || params[i];
const argValue = i < args.length ? args[i] : null;
environment.define(paramName, argValue);
}
try {
// Execute function body with parameter environment
const body = this.declaration.body || [];
// For arrow functions with expression body, the body is already wrapped in return
interpreter.executeBlock(body, environment);
} catch (returnValue) {
if (returnValue instanceof ReturnException) {
if (this.isInitializer) {
return this.closure.getAt(0, 'self');
}
return returnValue.value;
}
throw returnValue;
}
// If it's an initializer, return the instance
if (this.isInitializer) {
return this.closure.getAt(0, 'self');
}
// Default return null
return null;
}
bind(instance) {
const Environment = require('../interpreter/environment');
const environment = new Environment(this.closure);
environment.define('self', instance);
// If this is a method in a blueprint with inheritance, also define parent
if (instance && instance.blueprint && instance.blueprint.parentBlueprint !== null) {
const parentProxy = new ParentProxy(instance.blueprint.parentBlueprint, instance);
environment.define('parent', parentProxy);
}
return new PCFunction(this.declaration, environment, this.isInitializer);
}
toString() {
if (this.isArrowFunction) {
return `<arrow function ${this.name}>`;
}
return `<build ${this.name}>`;
}
}
class PCBlueprint {
constructor(name, parentBlueprint, methods) {
this.name = name;
this.parentBlueprint = parentBlueprint;
this.methods = methods || new Map();
}
arity() {
const initializer = this.findMethod('init');
if (initializer === null) return 0;
return initializer.arity();
}
call(interpreter, args) {
const instance = new PCInstance(this);
const initializer = this.findMethod('init');
if (initializer !== null) {
initializer.bind(instance).call(interpreter, args);
}
return instance;
}
findMethod(name) {
if (this.methods.has(name)) {
return this.methods.get(name);
}
if (this.parentBlueprint !== null) {
return this.parentBlueprint.findMethod(name);
}
return null;
}
toString() {
return this.name;
}
}
class PCInstance {
constructor(blueprint) {
this.blueprint = blueprint;
this.fields = new Map();
}
get(name) {
const propName = typeof name === 'string' ? name : (name.lexeme || name.name?.lexeme || name);
if (this.fields.has(propName)) {
return this.fields.get(propName);
}
const method = this.blueprint.findMethod(propName);
if (method !== null) return method.bind(this);
throw new Error(`Undefined property '${propName}'.`);
}
set(name, value) {
const propName = typeof name === 'string' ? name : (name.lexeme || name.name?.lexeme || name);
this.fields.set(propName, value);
}
toString() {
return `${this.blueprint.name} instance`;
}
}
// Parent proxy for blueprint inheritance
class ParentProxy {
constructor(blueprint, instance) {
this.blueprint = blueprint;
this.instance = instance;
}
findMethod(name) {
return this.blueprint.findMethod(name);
}
}
class ReturnException extends Error {
constructor(value) {
super();
this.value = value;
this.name = 'ReturnException';
}
}
class BreakException extends Error {
constructor() {
super();
this.name = 'BreakException';
}
}
class ContinueException extends Error {
constructor() {
super();
this.name = 'ContinueException';
}
}
module.exports = {
PCFunction,
PCBlueprint,
PCInstance,
ParentProxy,
ReturnException,
BreakException,
ContinueException
};