litenode
Version:
Lightweight and modular web framework
138 lines (120 loc) • 4.54 kB
JavaScript
import { BaseEvaluator } from "./BaseEvaluator.js"
export class StatementEvaluator extends BaseEvaluator {
constructor(state, evaluator) {
super(state)
this.evaluator = evaluator // Store reference to the main Evaluator
}
async evaluateSet(node) {
const value = await this.evaluator.evaluateNode(node.value)
if (node.propertyChain) {
// Get base object
let obj = this.resolveVariable(node.name)
if (!obj || typeof obj !== "object") throw new Error(`Cannot extend non-object variable: ${node.name}`)
// Navigate to the second-to-last object in the chain
let current = obj
for (let i = 0; i < node.propertyChain.length - 1; i++) {
const access = node.propertyChain[i]
const property = await this.evaluator.evaluateNode(access.property)
// Create nested object if it doesn't exist
if (!(property in current) || current[property] === null) current[property] = {}
else if (typeof current[property] !== "object")
throw new Error(`Cannot create nested property on non-object value`)
current = current[property]
}
// Set the final property
const lastAccess = node.propertyChain[node.propertyChain.length - 1]
const finalProperty = await this.evaluator.evaluateNode(lastAccess.property)
current[finalProperty] = value
} else {
if (node.name.startsWith("html_")) {
// For html_ variables, store both the raw value and a marker
const existingHtml = this.state.templateEngine.htmlVars.get(node.name)
const marker = existingHtml
? existingHtml.marker
: `__HTML_${Math.random().toString(36).substring(2, 11)}__`
this.state.templateEngine.htmlVars.set(node.name, { marker, value })
this.state.globalData[node.name] = marker
} else {
this.state.globalData[node.name] = value // Regular variable assignment, set the variable in globalData
}
}
return ""
}
async evaluateEach(node) {
const iterable = await this.evaluator.evaluateNode(node.iterable)
// Handle both arrays and objects
let result = ""
let index = 0
if (Array.isArray(iterable)) {
// Existing array handling
for (const item of iterable) {
this.state.contextStack.push(item)
this.state.indexStack.push(index)
this.state.keyStack.push(index.toString())
try {
const bodyResults = await Promise.all(node.body.map((n) => this.evaluator.evaluateNode(n)))
result += bodyResults.join("")
} finally {
this.state.contextStack.pop()
this.state.indexStack.pop()
this.state.keyStack.pop()
}
index++
}
} else if (iterable && typeof iterable === "object") {
// New object handling
for (const [key, value] of Object.entries(iterable)) {
this.state.contextStack.push(value)
this.state.indexStack.push(index)
this.state.keyStack.push(key)
try {
const bodyResults = await Promise.all(node.body.map((n) => this.evaluator.evaluateNode(n)))
result += bodyResults.join("")
} finally {
this.state.contextStack.pop()
this.state.indexStack.pop()
this.state.keyStack.pop()
}
index++
}
} else {
throw new Error(`Cannot iterate over ${iterable}`)
}
return result
}
async evaluateInclude(node) {
const pathValue = await this.evaluator.evaluateNode(node.path)
if (typeof pathValue !== "string") throw new Error("Include path must be a string")
const includedData = { ...this.state.globalData } // Create a new data object with all current data
return await this.state.templateEngine.renderStringWithoutRestore(pathValue, includedData)
}
async evaluateConditional(node) {
if (node.conditionType === "not") {
// Return body content only if condition evaluates to false
if (!(await this.evaluator.evaluateNode(node.condition))) {
// Evaluate body nodes sequentially
let result = ""
for (const n of node.body) result += await this.evaluator.evaluateNode(n)
return result
}
return ""
}
if (node.conditionType === "if") {
if (await this.evaluator.evaluateNode(node.condition)) {
// Evaluate body nodes sequentially
let result = ""
for (const n of node.body) result += await this.evaluator.evaluateNode(n)
return result
}
for (const alternate of node.alternates || []) {
if (alternate.conditionType === "else" || (await this.evaluator.evaluateNode(alternate.condition))) {
// Evaluate alternate nodes sequentially
let result = ""
for (const n of alternate.body) result += await this.evaluator.evaluateNode(n)
return result
}
}
}
return ""
}
}