/src/script/parser.ne
@preprocessor typescript
@{%
import * as moo from 'moo';
import { SayInstruction } from './say';
import { MenuInstruction } from './menu';
import { Label } from './label';
import { GotoInstruction } from './goto';
import { FinishInstruction } from './finish';
import { ClearScreenInstruction } from './clear';
import { SleepInstruction } from './sleep';
import { PauseInstruction } from './pause';
import { MoveInstruction } from './move';
import { Direction, directionFromToken } from '../direction';
const lexer = moo.compile({
ws: { match: /[ \t\r\n]+/, lineBreaks: true },
number: /[0-9]+(?:\.[0-9]+)?/,
keyword: ['say', 'menu', 'item', 'end', 'exit', 'goto', 'if', 'finish', 'clear', 'buy', 'transact', 'setflag', 'move'],
direction: /\+(?:n|e|s|w|north|east|south|west)/,
label: /[a-zA-Z_][a-zA-Z0-9_]*:/,
word: /[a-zA-Z_][a-zA-Z0-9_]*/,
dqstring: { match: /"(?:\\["\\]|[^\n"\\])*"/, value: s => s.slice(1, -1) },
sqstring: { match: /'(?:\\['\\]|[^\n'\\])*'/, value: s => s.slice(1, -1) },
mlstring: { match: /\[\[\[[^]*?\]\]\]/, lineBreaks: true, value: s => s.slice(3, -3).trim() },
bang: '!',
flag_mark: '$',
obj_mark: '@',
comma: ',',
});
%}
@lexer lexer
program -> __:? statement:* {% data => data[1] || data[0] %}
statement -> "say" __ string __ {% data => new SayInstruction(data[2]) %}
statement -> "menu" __ menuitem:* "end" __ {% data => new MenuInstruction(data[2]) %}
statement -> %label __ {% data => new Label(data[0].value.slice(0, -1)) %}
statement -> "goto" __ %word __ {% data => new GotoInstruction(data[2].value) %}
statement -> "finish" __ {% data => new FinishInstruction() %}
statement -> "clear" __ {% data => new ClearScreenInstruction() %}
statement -> "sleep" __ number __ {% data => new SleepInstruction(data[2]) %}
statement -> "pause" __ string __ {% data => new PauseInstruction(data[2]) %}
statement -> "move" __ direction __ {% data => new MoveInstruction(data[2]) %}
statement -> "move" __ number _ %comma _ number __ {% data => new MoveInstruction(data[2], data[6]) %}
menuitem -> if:? "item" __ string __ program "end" __
menuitem -> if:? "buy" __ obj_identifier __ number __
menuitem -> if:? "exit" __ string __
if -> "if" __ %bang:? flag_identifier __
if -> "if" __ %bang:? obj_identifier __
flag_identifier -> %flag_mark %word
obj_identifier -> %obj_mark %word
number -> %number {% data => parseFloat(data[0]) %}
string -> %dqstring {% data => data[0].value %}
string -> %sqstring {% data => data[0].value %}
string -> %mlstring {% data => data[0].value %}
direction -> %direction {% data => directionFromToken(data[0]) %}
__ -> %ws {% _ => null %}
_ -> null | %ws {% _ => null %}