petcarescript
Version:
PetCareScript - A modern, expressive programming language designed for humans
1,422 lines (1,266 loc) • 112 kB
JavaScript
/**
* PetCareScript Interpreter
* Executes the abstract syntax tree - VERSÃO MELHORADA E OTIMIZADA
*/
const { TokenType } = require('../lexer/tokens');
const Environment = require('./environment');
const {
PCFunction,
PCBlueprint,
PCInstance,
ParentProxy,
ReturnException,
BreakException,
ContinueException
} = require('../runtime/runtime');
class Interpreter {
constructor() {
this.globals = new Environment();
this.environment = this.globals;
this.locals = new Map();
this.moduleCache = new Map();
this.defineNatives();
}
// Métodos de módulos melhorados
importModule(modulePath) {
const fs = require('fs');
const path = require('path');
let resolvedPath = this.resolveModulePath(modulePath);
if (this.moduleCache.has(resolvedPath)) {
return this.moduleCache.get(resolvedPath);
}
try {
const source = fs.readFileSync(resolvedPath, 'utf8');
const moduleEnvironment = new Environment(this.globals);
const previousEnv = this.environment;
this.environment = moduleEnvironment;
const result = this.run(source);
this.environment = previousEnv;
this.moduleCache.set(resolvedPath, moduleEnvironment);
return moduleEnvironment;
} catch (error) {
throw new Error(`Failed to import module '${modulePath}': ${error.message}`);
}
}
requireModule(modulePath) {
try {
return require(modulePath);
} catch (error) {
return this.importModule(modulePath);
}
}
exportValue(value) {
if (!this.environment.exports) {
this.environment.exports = {};
}
Object.assign(this.environment.exports, value);
return value;
}
resolveModulePath(modulePath) {
const fs = require('fs');
const path = require('path');
if (modulePath.startsWith('./') || modulePath.startsWith('../')) {
const currentDir = process.cwd();
let fullPath = path.resolve(currentDir, modulePath);
if (!fullPath.endsWith('.pcs')) {
fullPath += '.pcs';
}
if (fs.existsSync(fullPath)) {
return fullPath;
}
}
if (path.isAbsolute(modulePath)) {
let fullPath = modulePath;
if (!fullPath.endsWith('.pcs')) {
fullPath += '.pcs';
}
if (fs.existsSync(fullPath)) {
return fullPath;
}
}
const coreModules = {
'core': path.join(__dirname, '../stdlib/core.pcs'),
'http': path.join(__dirname, '../stdlib/http.pcs'),
'database': path.join(__dirname, '../stdlib/database.pcs'),
'testing': path.join(__dirname, '../stdlib/testing.pcs'),
'fs': path.join(__dirname, '../stdlib/fs.pcs'),
'cli': path.join(__dirname, '../stdlib/cli.pcs')
};
if (coreModules[modulePath]) {
return coreModules[modulePath];
}
const nodeModulesPath = path.join(process.cwd(), 'node_modules', modulePath);
if (fs.existsSync(nodeModulesPath)) {
return nodeModulesPath;
}
const pcsModulesPath = path.join(process.cwd(), 'pcs_modules', modulePath);
if (fs.existsSync(pcsModulesPath)) {
return pcsModulesPath;
}
throw new Error(`Module not found: ${modulePath}`);
}
defineNatives() {
// Core time functions
this.globals.define('currentTime', {
arity: () => 0,
call: () => Date.now() / 1000.0
});
this.globals.define('now', {
arity: () => 0,
call: () => new Date()
});
this.globals.define('timestamp', {
arity: () => 0,
call: () => Date.now()
});
// Enhanced String/Array functions
this.globals.define('contains', {
arity: () => 2,
call: (interpreter, args) => {
const [haystack, needle] = args;
if (typeof haystack === 'string') {
return haystack.includes(needle);
}
if (Array.isArray(haystack)) {
return haystack.includes(needle);
}
if (typeof haystack === 'object' && haystack !== null) {
return haystack.hasOwnProperty(needle);
}
return false;
}
});
// Enhanced type checking functions
this.globals.define('typeOf', {
arity: () => 1,
call: (interpreter, args) => {
const value = args[0];
if (value === null || value === undefined) return 'empty';
if (Array.isArray(value)) return 'array';
if (value instanceof Date) return 'date';
if (value instanceof RegExp) return 'regex';
return typeof value;
}
});
// Enhanced type conversion functions
this.globals.define('toNumber', {
arity: () => 1,
call: (interpreter, args) => {
const value = args[0];
if (typeof value === 'number') return value;
if (typeof value === 'string') {
const num = parseFloat(value);
if (isNaN(num)) throw new Error('Cannot convert to number');
return num;
}
if (typeof value === 'boolean') return value ? 1 : 0;
if (value instanceof Date) return value.getTime();
throw new Error('Cannot convert to number');
}
});
this.globals.define('toString', {
arity: () => 1,
call: (interpreter, args) => {
const value = args[0];
return this.stringify(value);
}
});
this.globals.define('toBoolean', {
arity: () => 1,
call: (interpreter, args) => {
const value = args[0];
if (typeof value === 'boolean') return value;
if (value === null || value === undefined) return false;
if (typeof value === 'number') return value !== 0;
if (typeof value === 'string') return value.length > 0;
if (Array.isArray(value)) return value.length > 0;
if (typeof value === 'object') return Object.keys(value).length > 0;
return true;
}
});
this.globals.define('toArray', {
arity: () => 1,
call: (interpreter, args) => {
const value = args[0];
if (Array.isArray(value)) return value;
if (typeof value === 'string') return value.split('');
if (typeof value === 'object' && value !== null) return Object.values(value);
return [value];
}
});
// Enhanced type checking predicates
this.globals.define('isNumber', {
arity: () => 1,
call: (interpreter, args) => typeof args[0] === 'number' && !isNaN(args[0])
});
this.globals.define('isString', {
arity: () => 1,
call: (interpreter, args) => typeof args[0] === 'string'
});
this.globals.define('isBoolean', {
arity: () => 1,
call: (interpreter, args) => typeof args[0] === 'boolean'
});
this.globals.define('isArray', {
arity: () => 1,
call: (interpreter, args) => Array.isArray(args[0])
});
this.globals.define('isObject', {
arity: () => 1,
call: (interpreter, args) => {
const value = args[0];
return typeof value === 'object' && value !== null && !Array.isArray(value) && !(value instanceof Date);
}
});
this.globals.define('isFunction', {
arity: () => 1,
call: (interpreter, args) => {
const value = args[0];
return typeof value === 'function' || (value && typeof value.call === 'function');
}
});
this.globals.define('isDate', {
arity: () => 1,
call: (interpreter, args) => args[0] instanceof Date
});
this.globals.define('isNull', {
arity: () => 1,
call: (interpreter, args) => args[0] === null
});
this.globals.define('isUndefined', {
arity: () => 1,
call: (interpreter, args) => args[0] === undefined
});
this.globals.define('isEmpty', {
arity: () => 1,
call: (interpreter, args) => {
const value = args[0];
if (value === null || value === undefined) return true;
if (typeof value === 'string') return value.length === 0;
if (Array.isArray(value)) return value.length === 0;
if (typeof value === 'object' && value !== null) return Object.keys(value).length === 0;
if (typeof value === 'number') return value === 0;
if (typeof value === 'boolean') return !value;
return false;
}
});
this.globals.define('isNaN', {
arity: () => 1,
call: (interpreter, args) => isNaN(args[0])
});
this.globals.define('isFinite', {
arity: () => 1,
call: (interpreter, args) => isFinite(args[0])
});
// Size/length functions
this.globals.define('length', {
arity: () => 1,
call: (interpreter, args) => {
const value = args[0];
if (typeof value === 'string' || Array.isArray(value)) {
return value.length;
}
if (typeof value === 'object' && value !== null) {
return Object.keys(value).length;
}
throw new Error('Value does not support length operation');
}
});
this.globals.define('size', {
arity: () => 1,
call: (interpreter, args) => {
return this.globals.get('length').call(interpreter, args);
}
});
// Enhanced Array functions
this.globals.define('push', {
arity: () => 2,
call: (interpreter, args) => {
const [array, element] = args;
if (!Array.isArray(array)) throw new Error('First argument must be an array');
array.push(element);
return array;
}
});
this.globals.define('pop', {
arity: () => 1,
call: (interpreter, args) => {
const [array] = args;
if (!Array.isArray(array)) throw new Error('Argument must be an array');
return array.pop();
}
});
this.globals.define('shift', {
arity: () => 1,
call: (interpreter, args) => {
const [array] = args;
if (!Array.isArray(array)) throw new Error('Argument must be an array');
return array.shift();
}
});
this.globals.define('unshift', {
arity: () => 2,
call: (interpreter, args) => {
const [array, element] = args;
if (!Array.isArray(array)) throw new Error('First argument must be an array');
array.unshift(element);
return array;
}
});
this.globals.define('slice', {
arity: () => -1,
call: (interpreter, args) => {
const [array, start = 0, end] = args;
if (!Array.isArray(array) && typeof array !== 'string') {
throw new Error('First argument must be an array or string');
}
return array.slice(start, end);
}
});
this.globals.define('splice', {
arity: () => -1,
call: (interpreter, args) => {
const [array, start, deleteCount, ...items] = args;
if (!Array.isArray(array)) throw new Error('First argument must be an array');
return array.splice(start, deleteCount, ...items);
}
});
this.globals.define('indexOf', {
arity: () => 2,
call: (interpreter, args) => {
const [array, element] = args;
if (!Array.isArray(array) && typeof array !== 'string') {
throw new Error('First argument must be an array or string');
}
return array.indexOf(element);
}
});
this.globals.define('lastIndexOf', {
arity: () => 2,
call: (interpreter, args) => {
const [array, element] = args;
if (!Array.isArray(array) && typeof array !== 'string') {
throw new Error('First argument must be an array or string');
}
return array.lastIndexOf(element);
}
});
this.globals.define('includes', {
arity: () => 2,
call: (interpreter, args) => {
const [array, element] = args;
if (!Array.isArray(array) && typeof array !== 'string') {
throw new Error('First argument must be an array or string');
}
return array.includes(element);
}
});
this.globals.define('reverse', {
arity: () => 1,
call: (interpreter, args) => {
const [array] = args;
if (!Array.isArray(array)) throw new Error('Argument must be an array');
return array.reverse();
}
});
this.globals.define('sort', {
arity: () => -1,
call: (interpreter, args) => {
const [array, compareFn] = args;
if (!Array.isArray(array)) throw new Error('First argument must be an array');
if (compareFn) {
return array.sort((a, b) => {
if (compareFn && typeof compareFn.call === 'function') {
return compareFn.call(interpreter, [a, b]);
}
return 0;
});
}
return array.sort();
}
});
// NEW: sortBy function
this.globals.define('sortBy', {
arity: () => 2,
call: (interpreter, args) => {
const [array, keyFn] = args;
if (!Array.isArray(array)) throw new Error('First argument must be an array');
return array.sort((a, b) => {
let keyA, keyB;
if (typeof keyFn === 'string') {
keyA = a[keyFn];
keyB = b[keyFn];
} else if (keyFn && typeof keyFn.call === 'function') {
keyA = keyFn.call(interpreter, [a]);
keyB = keyFn.call(interpreter, [b]);
} else {
keyA = a;
keyB = b;
}
if (keyA < keyB) return -1;
if (keyA > keyB) return 1;
return 0;
});
}
});
this.globals.define('find', {
arity: () => 2,
call: (interpreter, args) => {
const [array, predicate] = args;
if (!Array.isArray(array)) throw new Error('First argument must be an array');
return array.find(item => {
if (predicate && typeof predicate.call === 'function') {
return predicate.call(interpreter, [item]);
}
return false;
});
}
});
this.globals.define('findIndex', {
arity: () => 2,
call: (interpreter, args) => {
const [array, predicate] = args;
if (!Array.isArray(array)) throw new Error('First argument must be an array');
return array.findIndex(item => {
if (predicate && typeof predicate.call === 'function') {
return predicate.call(interpreter, [item]);
}
return false;
});
}
});
this.globals.define('filter', {
arity: () => 2,
call: (interpreter, args) => {
const [array, predicate] = args;
if (!Array.isArray(array)) throw new Error('First argument must be an array');
return array.filter(item => {
if (predicate && typeof predicate.call === 'function') {
return predicate.call(interpreter, [item]);
}
return false;
});
}
});
this.globals.define('map', {
arity: () => 2,
call: (interpreter, args) => {
const [array, mapFn] = args;
if (!Array.isArray(array)) throw new Error('First argument must be an array');
return array.map(item => {
if (mapFn && typeof mapFn.call === 'function') {
return mapFn.call(interpreter, [item]);
}
return item;
});
}
});
this.globals.define('forEach', {
arity: () => 2,
call: (interpreter, args) => {
const [array, fn] = args;
if (!Array.isArray(array)) throw new Error('First argument must be an array');
array.forEach(item => {
if (fn && typeof fn.call === 'function') {
fn.call(interpreter, [item]);
}
});
return null;
}
});
// NEW: every function
this.globals.define('every', {
arity: () => 2,
call: (interpreter, args) => {
const [array, predicate] = args;
if (!Array.isArray(array)) throw new Error('First argument must be an array');
return array.every(item => {
if (predicate && typeof predicate.call === 'function') {
return predicate.call(interpreter, [item]);
}
return false;
});
}
});
// NEW: some function
this.globals.define('some', {
arity: () => 2,
call: (interpreter, args) => {
const [array, predicate] = args;
if (!Array.isArray(array)) throw new Error('First argument must be an array');
return array.some(item => {
if (predicate && typeof predicate.call === 'function') {
return predicate.call(interpreter, [item]);
}
return false;
});
}
});
this.globals.define('reduce', {
arity: () => -1,
call: (interpreter, args) => {
const [array, reduceFn, initialValue] = args;
if (!Array.isArray(array)) throw new Error('First argument must be an array');
if (args.length > 2) {
return array.reduce((acc, current) => {
if (reduceFn && typeof reduceFn.call === 'function') {
return reduceFn.call(interpreter, [acc, current]);
}
return acc;
}, initialValue);
} else {
return array.reduce((acc, current) => {
if (reduceFn && typeof reduceFn.call === 'function') {
return reduceFn.call(interpreter, [acc, current]);
}
return acc;
});
}
}
});
this.globals.define('join', {
arity: () => 2,
call: (interpreter, args) => {
const [array, separator] = args;
if (!Array.isArray(array)) throw new Error('First argument must be an array');
return array.join(separator || ',');
}
});
this.globals.define('concat', {
arity: () => -1,
call: (interpreter, args) => {
const [array, ...others] = args;
if (!Array.isArray(array)) throw new Error('First argument must be an array');
return array.concat(...others);
}
});
// Enhanced String functions
this.globals.define('upper', {
arity: () => 1,
call: (interpreter, args) => {
const [str] = args;
if (typeof str !== 'string') throw new Error('Argument must be a string');
return str.toUpperCase();
}
});
this.globals.define('lower', {
arity: () => 1,
call: (interpreter, args) => {
const [str] = args;
if (typeof str !== 'string') throw new Error('Argument must be a string');
return str.toLowerCase();
}
});
this.globals.define('trim', {
arity: () => 1,
call: (interpreter, args) => {
const [str] = args;
if (typeof str !== 'string') throw new Error('Argument must be a string');
return str.trim();
}
});
this.globals.define('split', {
arity: () => 2,
call: (interpreter, args) => {
const [str, separator] = args;
if (typeof str !== 'string') throw new Error('First argument must be a string');
return str.split(separator);
}
});
this.globals.define('replace', {
arity: () => 3,
call: (interpreter, args) => {
const [str, search, replacement] = args;
if (typeof str !== 'string') throw new Error('First argument must be a string');
return str.replace(search, replacement);
}
});
this.globals.define('charAt', {
arity: () => 2,
call: (interpreter, args) => {
const [str, index] = args;
if (typeof str !== 'string') throw new Error('First argument must be a string');
return str.charAt(index);
}
});
this.globals.define('substring', {
arity: () => -1,
call: (interpreter, args) => {
const [str, start, end] = args;
if (typeof str !== 'string') throw new Error('First argument must be a string');
return str.substring(start, end);
}
});
this.globals.define('startsWith', {
arity: () => 2,
call: (interpreter, args) => {
const [str, prefix] = args;
if (typeof str !== 'string') throw new Error('First argument must be a string');
return str.startsWith(prefix);
}
});
this.globals.define('endsWith', {
arity: () => 2,
call: (interpreter, args) => {
const [str, suffix] = args;
if (typeof str !== 'string') throw new Error('First argument must be a string');
return str.endsWith(suffix);
}
});
// ENHANCED OBJECT GLOBAL - VERSÃO COMPLETA E CORRIGIDA
this.globals.define('Object', {
keys: {
arity: () => 1,
call: (interpreter, args) => {
const [obj] = args;
if (typeof obj !== 'object' || obj === null) {
throw new Error('Argument must be an object');
}
return Object.keys(obj);
}
},
values: {
arity: () => 1,
call: (interpreter, args) => {
const [obj] = args;
if (typeof obj !== 'object' || obj === null) {
throw new Error('Argument must be an object');
}
return Object.values(obj);
}
},
entries: {
arity: () => 1,
call: (interpreter, args) => {
const [obj] = args;
if (typeof obj !== 'object' || obj === null) {
throw new Error('Argument must be an object');
}
return Object.entries(obj);
}
},
assign: {
arity: () => -1,
call: (interpreter, args) => {
if (args.length < 1) {
throw new Error('Object.assign requires at least 1 argument');
}
const target = args[0];
const sources = args.slice(1);
if (typeof target !== 'object' || target === null) {
if (sources.length > 0 && typeof sources[0] === 'object' && sources[0] !== null) {
return Object.assign({}, ...sources);
}
return {};
}
for (const source of sources) {
if (source !== null && source !== undefined) {
Object.assign(target, source);
}
}
return target;
}
},
hasOwnProperty: {
arity: () => 2,
call: (interpreter, args) => {
const [obj, prop] = args;
if (typeof obj !== 'object' || obj === null) {
return false;
}
return Object.prototype.hasOwnProperty.call(obj, prop);
}
},
create: {
arity: () => -1,
call: (interpreter, args) => {
const [proto, properties] = args;
const obj = Object.create(proto);
if (properties && typeof properties === 'object') {
Object.assign(obj, properties);
}
return obj;
}
},
freeze: {
arity: () => 1,
call: (interpreter, args) => {
const [obj] = args;
if (typeof obj !== 'object' || obj === null) {
throw new Error('Argument must be an object');
}
return Object.freeze(obj);
}
},
seal: {
arity: () => 1,
call: (interpreter, args) => {
const [obj] = args;
if (typeof obj !== 'object' || obj === null) {
throw new Error('Argument must be an object');
}
return Object.seal(obj);
}
},
preventExtensions: {
arity: () => 1,
call: (interpreter, args) => {
const [obj] = args;
if (typeof obj !== 'object' || obj === null) {
throw new Error('Argument must be an object');
}
return Object.preventExtensions(obj);
}
},
isFrozen: {
arity: () => 1,
call: (interpreter, args) => {
const [obj] = args;
if (typeof obj !== 'object' || obj === null) {
return true;
}
return Object.isFrozen(obj);
}
},
isSealed: {
arity: () => 1,
call: (interpreter, args) => {
const [obj] = args;
if (typeof obj !== 'object' || obj === null) {
return true;
}
return Object.isSealed(obj);
}
},
isExtensible: {
arity: () => 1,
call: (interpreter, args) => {
const [obj] = args;
if (typeof obj !== 'object' || obj === null) {
return false;
}
return Object.isExtensible(obj);
}
},
getOwnPropertyNames: {
arity: () => 1,
call: (interpreter, args) => {
const [obj] = args;
if (typeof obj !== 'object' || obj === null) {
throw new Error('Argument must be an object');
}
return Object.getOwnPropertyNames(obj);
}
},
getOwnPropertyDescriptor: {
arity: () => 2,
call: (interpreter, args) => {
const [obj, prop] = args;
if (typeof obj !== 'object' || obj === null) {
return undefined;
}
return Object.getOwnPropertyDescriptor(obj, prop);
}
},
defineProperty: {
arity: () => 3,
call: (interpreter, args) => {
const [obj, prop, descriptor] = args;
if (typeof obj !== 'object' || obj === null) {
throw new Error('First argument must be an object');
}
return Object.defineProperty(obj, prop, descriptor);
}
}
});
// ENHANCED GLOBAL FUNCTIONS FOR OBJECT COMPATIBILITY
this.globals.define('keys', {
arity: () => 1,
call: (interpreter, args) => {
const [obj] = args;
if (typeof obj !== 'object' || obj === null) {
throw new Error('Argument must be an object');
}
return Object.keys(obj);
}
});
this.globals.define('values', {
arity: () => 1,
call: (interpreter, args) => {
const [obj] = args;
if (typeof obj !== 'object' || obj === null) {
throw new Error('Argument must be an object');
}
return Object.values(obj);
}
});
this.globals.define('entries', {
arity: () => 1,
call: (interpreter, args) => {
const [obj] = args;
if (typeof obj !== 'object' || obj === null) {
throw new Error('Argument must be an object');
}
return Object.entries(obj);
}
});
// NEW: hasProperty function - CORRIGIDO
this.globals.define('hasProperty', {
arity: () => 2,
call: (interpreter, args) => {
const [obj, prop] = args;
if (typeof obj !== 'object' || obj === null) {
return false;
}
return Object.prototype.hasOwnProperty.call(obj, prop);
}
});
this.globals.define('hasOwnProperty', {
arity: () => 2,
call: (interpreter, args) => {
const [obj, prop] = args;
if (typeof obj !== 'object' || obj === null) {
return false;
}
return Object.prototype.hasOwnProperty.call(obj, prop);
}
});
// ENHANCED JSON functions
this.globals.define('parseJSON', {
arity: () => 1,
call: (interpreter, args) => {
const [str] = args;
try {
return JSON.parse(str);
} catch (error) {
throw new Error('Invalid JSON: ' + error.message);
}
}
});
this.globals.define('stringifyJSON', {
arity: () => -1,
call: (interpreter, args) => {
const [obj, replacer, space] = args;
try {
return JSON.stringify(obj, replacer, space);
} catch (error) {
throw new Error('Cannot stringify to JSON: ' + error.message);
}
}
});
// ENHANCED JSON global object
this.globals.define('JSON', {
parse: {
arity: () => 1,
call: (interpreter, args) => {
const [str] = args;
try {
return JSON.parse(str);
} catch (error) {
throw new Error('Invalid JSON: ' + error.message);
}
}
},
stringify: {
arity: () => -1,
call: (interpreter, args) => {
const [obj, replacer, space] = args;
try {
return JSON.stringify(obj, replacer, space);
} catch (error) {
throw new Error('Cannot stringify to JSON: ' + error.message);
}
}
}
});
// Enhanced Math functions
this.globals.define('abs', {
arity: () => 1,
call: (interpreter, args) => Math.abs(args[0])
});
this.globals.define('max', {
arity: () => -1,
call: (interpreter, args) => Math.max(...args)
});
this.globals.define('min', {
arity: () => -1,
call: (interpreter, args) => Math.min(...args)
});
this.globals.define('sqrt', {
arity: () => 1,
call: (interpreter, args) => Math.sqrt(args[0])
});
this.globals.define('pow', {
arity: () => 2,
call: (interpreter, args) => Math.pow(args[0], args[1])
});
this.globals.define('floor', {
arity: () => 1,
call: (interpreter, args) => Math.floor(args[0])
});
this.globals.define('ceil', {
arity: () => 1,
call: (interpreter, args) => Math.ceil(args[0])
});
this.globals.define('round', {
arity: () => 1,
call: (interpreter, args) => Math.round(args[0])
});
this.globals.define('random', {
arity: () => 0,
call: (interpreter, args) => Math.random()
});
this.globals.define('randomInt', {
arity: () => 2,
call: (interpreter, args) => {
const [min, max] = args;
return Math.floor(Math.random() * (max - min + 1)) + min;
}
});
// Enhanced Module system
this.globals.define('import', {
arity: () => 1,
call: (interpreter, args) => {
const modulePath = args[0];
return interpreter.importModule(modulePath);
}
});
this.globals.define('export', {
arity: () => 1,
call: (interpreter, args) => {
const exportValue = args[0];
return interpreter.exportValue(exportValue);
}
});
this.globals.define('require', {
arity: () => 1,
call: (interpreter, args) => {
const modulePath = args[0];
return interpreter.requireModule(modulePath);
}
});
// Database functions
this.globals.define('connect', {
arity: () => 1,
call: (interpreter, args) => {
const filename = args[0];
return this.createMockDatabase(filename);
}
});
this.globals.define('types', {
arity: () => 0,
call: (interpreter, args) => {
return {
INTEGER: 'INTEGER',
TEXT: 'TEXT',
REAL: 'REAL',
BLOB: 'BLOB',
PRIMARY_KEY: 'INTEGER PRIMARY KEY AUTOINCREMENT',
TIMESTAMP: 'DATETIME DEFAULT CURRENT_TIMESTAMP',
BOOLEAN: 'INTEGER',
JSON: 'TEXT'
};
}
});
// HTTP/Server related functions
this.globals.define('createServer', {
arity: () => 1,
call: (interpreter, args) => {
const port = args[0] || 3000;
return this.createMockServer(port, interpreter);
}
});
this.globals.define('get', {
arity: () => -1,
call: (interpreter, args) => {
const url = args[0];
return Promise.resolve({
data: `Mock response from ${url}`,
status: 200,
headers: {}
});
}
});
// Enhanced middleware functions
this.globals.define('cors', {
arity: () => -1,
call: (interpreter, args) => {
const options = args[0] || {};
const origin = options.origin || '*';
const methods = options.methods || 'GET,HEAD,PUT,PATCH,POST,DELETE';
const allowedHeaders = options.allowedHeaders || 'Content-Type,Authorization';
return {
name: 'CORS',
type: 'middleware',
arity: () => 3,
call: (interpreterInstance, middlewareArgs) => {
const [req, res, next] = middlewareArgs;
console.log('🔧 CORS middleware applied');
res.headers = res.headers || {};
res.headers['Access-Control-Allow-Origin'] = origin;
res.headers['Access-Control-Allow-Methods'] = methods;
res.headers['Access-Control-Allow-Headers'] = allowedHeaders;
if (req.method === 'OPTIONS') {
res.statusCode = 200;
console.log('🔧 CORS preflight response sent');
}
if (next && typeof next.call === 'function') {
next.call(interpreterInstance, []);
}
return null;
},
toString: () => '[Middleware: CORS]'
};
}
});
this.globals.define('json', {
arity: () => -1,
call: (interpreter, args) => {
const options = args[0] || {};
const limit = options.limit || '100kb';
return {
name: 'JSON',
type: 'middleware',
arity: () => 3,
call: (interpreterInstance, middlewareArgs) => {
const [req, res, next] = middlewareArgs;
console.log('🔧 JSON middleware applied');
if (req.body && typeof req.body === 'string') {
try {
req.body = JSON.parse(req.body);
console.log('🔧 JSON body parsed');
} catch (error) {
console.error('🔧 JSON parse error:', error.message);
}
}
res.headers = res.headers || {};
res.headers['Content-Type'] = 'application/json';
if (next && typeof next.call === 'function') {
next.call(interpreterInstance, []);
}
return null;
},
toString: () => '[Middleware: JSON]'
};
}
});
this.globals.define('logger', {
arity: () => -1,
call: (interpreter, args) => {
const options = args[0] || {};
const format = options.format || 'combined';
return {
name: 'Logger',
type: 'middleware',
arity: () => 3,
call: (interpreterInstance, middlewareArgs) => {
const [req, res, next] = middlewareArgs;
const timestamp = new Date().toISOString();
const userAgent = req.headers['user-agent'] || '-';
const contentLength = req.headers['content-length'] || '-';
if (format === 'combined') {
console.log(`🔧 [${timestamp}] ${req.method} ${req.path} - ${req.headers['x-forwarded-for'] || '127.0.0.1'} "${userAgent}" ${contentLength}`);
} else {
console.log(`🔧 [${timestamp}] ${req.method} ${req.path}`);
}
if (next && typeof next.call === 'function') {
next.call(interpreterInstance, []);
}
return null;
},
toString: () => '[Middleware: Logger]'
};
}
});
// Enhanced Promise functions
this.globals.define('Promise', {
arity: () => 1,
call: (interpreter, args) => {
const executor = args[0];
return new Promise((resolve, reject) => {
try {
if (executor && typeof executor.call === 'function') {
executor.call(interpreter, [resolve, reject]);
} else if (typeof executor === 'function') {
executor(resolve, reject);
} else {
reject(new Error('Promise executor must be a function'));
}
} catch (error) {
reject(error);
}
});
}
});
// NEW: Enhanced Async functions - timeout, retry, parallel
this.globals.define('timeout', {
arity: () => 2,
call: (interpreter, args) => {
const [fn, delay] = args;
return setTimeout(() => {
if (fn && typeof fn.call === 'function') {
fn.call(interpreter, []);
} else if (typeof fn === 'function') {
fn();
}
}, delay);
}
});
// NEW: retry function
this.globals.define('retry', {
arity: () => -1,
call: async (interpreter, args) => {
const [fn, maxRetries = 3, delayMs = 1000] = args;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
if (fn && typeof fn.call === 'function') {
return await fn.call(interpreter, []);
} else if (typeof fn === 'function') {
return await fn();
}
} catch (error) {
if (attempt === maxRetries) {
throw new Error(`Failed after ${maxRetries} attempts: ${error.message}`);
}
// Wait before retry
await new Promise(resolve => setTimeout(resolve, delayMs));
}
}
}
});
// NEW: Enhanced parallel function
this.globals.define('parallel', {
arity: () => 1,
call: (interpreter, args) => {
const tasks = args[0];
if (!Array.isArray(tasks)) {
throw new Error('Argument must be an array of tasks');
}
return Promise.all(tasks.map(task => {
if (task && typeof task.call === 'function') {
return task.call(interpreter, []);
} else if (typeof task === 'function') {
return task();
}
return task;
}));
}
});
// Promise helpers
this.globals.define('all', {
arity: () => 1,
call: (interpreter, args) => {
const promises = args[0];
return Promise.all(promises);
}
});
// Enhanced Console functions
this.globals.define('log', {
arity: () => -1,
call: (interpreter, args) => {
console.log(...args.map(arg => this.stringify(arg)));
return null;
}
});
this.globals.define('error', {
arity: () => -1,
call: (interpreter, args) => {
console.error(...args.map(arg => this.stringify(arg)));
return null;
}
});
this.globals.define('warn', {
arity: () => -1,
call: (interpreter, args) => {
console.warn(...args.map(arg => this.stringify(arg)));
return null;
}
});
this.globals.define('info', {
arity: () => -1,
call: (interpreter, args) => {
console.info(...args.map(arg => this.stringify(arg)));
return null;
}
});
// Date functions
this.globals.define('createDate', {
arity: () => -1,
call: (interpreter, args) => {
if (args.length === 0) {
return new Date();
}
return new Date(...args);
}
});
this.globals.define('formatDate', {
arity: () => -1,
call: (interpreter, args) => {
const [date, format] = args;
if (!(date instanceof Date)) {
throw new Error('First argument must be a date');
}
if (format) {
return date.toLocaleDateString(undefined, format);
}
return date.toISOString();
}
});
// Utility functions
this.globals.define('range', {
arity: () => -1,
call: (interpreter, args) => {
const [start, end, step = 1] = args;
const result = [];
for (let i = start; i < end; i += step) {
result.push(i);
}
return result;
}
});
this.globals.define('repeat', {
arity: () => 2,
call: (interpreter, args) => {
const [fn, times] = args;
const results = [];
for (let i = 0; i < times; i++) {
if (fn && typeof fn.call === 'function') {
results.push(fn.call(interpreter, [i]));
}
}
return results;
}
});
this.globals.define('sleep', {
arity: () => 1,
call: (interpreter, args) => {
const [ms] = args;
return new Promise(resolve => setTimeout(resolve, ms));
}
});
}
// ENHANCED native method detection for arrays, strings, and objects
getNativeMethod(object, methodName) {
if (Array.isArray(object)) {
switch (methodName) {
case 'push':
return {
arity: () => 1,
call: (interpreter, args) => {
const element = args[0];
object.push(element);
return object;
}
};
case 'pop':
return {
arity: () => 0,
call: (interpreter, args) => object.pop()
};
case 'shift':
return {
arity: () => 0,
call: (interpreter, args) => object.shift()
};
case 'unshift':
return {