cat << 'EOF' > compiler.js
export class SemanticCompiler {
constructor() {
this.baseUrl = "http://100.111.184.107:8080/v1/chat/completions";
this.model = "gemma-2-9b-it";
}
async compile(naturalLanguageIntent) {
const systemPrompt = `
You are a strict code compiler for interactive text adventure objects.
Translate the user's item description into Flat Semantic Opcodes (FSO).
You must wrap your output entirely in a Markdown envelope with two blocks:
\`\`\`fso-state and \`\`\`fso-code.
AVAILABLE OPCODES:
- VAR [NAME] [TYPE] [VALUE] (Types: STR, NUM, BOOL, ARR)
- SET [NAME] [VALUE]
- MATH [NAME] [OPERATOR] [VALUE] (Operators: +, -, *, /)
- LBL [NAME]
- JMP [LABEL]
- JMP_IF [VAR] [IS/NOT/CONTAINS/HAS] "[VALUE]" [LABEL]
- OUT "[STRING]"
- ACTION "[VERB]" [LABEL] (Registers a room-local verb)
- GLOBAL "[VERB]" [LABEL] (Registers a universal verb)
- AWAIT (Yields execution back to the player)
ENVIRONMENT VARIABLES:
To modify or read the state of the room the item is currently in, prefix the
variable name with ENVIRONMENT_ (e.g., ENVIRONMENT_LIGHT, ENVIRONMENT_TEMPERATU
RE). The engine will automatically route this to the player's physical
location.
ARCHITECTURE RULES:
1. Always define an init label (e.g., LBL init_itemname) that registers the
item's ACTION verbs, then ends with AWAIT.
2. Every action label must execute its logic, print an OUT string (you can
interpolate variables using {VAR_NAME}), and end with AWAIT.
3. Use JMP_IF to prevent repeating actions (e.g., don't open an open box).
4. Do NOT output conversational text. Output ONLY the markdown envelope.
EXAMPLE OUTPUT:
\`\`\`fso-state
VAR MUSICBOX_OPEN BOOL "false"
\`\`\`
\`\`\`fso-code
LBL init_musicbox
ACTION "OPEN" act_musicbox_open
ACTION "CLOSE" act_musicbox_close
AWAIT
LBL act_musicbox_open
JMP_IF MUSICBOX_OPEN == "true" musicbox_already_open
SET MUSICBOX_OPEN "true"
MATH ENVIRONMENT_SPOOKINESS + 1
OUT "You open the box. A creepy melody plays. Spookiness is now
{ENVIRONMENT_SPOOKINESS}."
AWAIT
LBL musicbox_already_open
OUT "The box is already open."
AWAIT
LBL act_musicbox_close
JMP_IF MUSICBOX_OPEN == "false" musicbox_already_closed
SET MUSICBOX_OPEN "false"
MATH ENVIRONMENT_SPOOKINESS - 1
OUT "You snap the box shut. The music stops."
AWAIT
LBL musicbox_already_closed
OUT "It is already closed."
AWAIT
\`\`\`
`.trim();
const payload = {
model: this.model,
messages: [
{ role: "system", content: systemPrompt },
{ role: "user", content: naturalLanguageIntent }
],
temperature: 0.2 // Slightly higher to allow creative text in OUT
strings
};
try {
const response = await fetch(this.baseUrl, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload)
});
if (!response.ok) throw new Error(`Cluster Error:
${response.statusText}`);
const data = await response.json();
// Clean out Gemma's trailing stop tokens in case they leak
let content = data.choices[0].message.content;
return content.replace(/<[^>]+>/g, '').trim();
} catch (error) {
console.error("
[COMPILER FAULT] Could not reach Gemma cluster:", error);
throw error;
}
}
}
EOF