From 8a5684c4ec3c23a894a7be06b5e20a3522c23026 Mon Sep 17 00:00:00 2001 From: xwashere Date: Mon, 27 Nov 2023 10:18:15 -0500 Subject: [PATCH] template inclusion --- backend/templates/error.ts | 52 +++++++++++++++++++++++++++++++++++-- backend/templates/parser.ts | 29 ++++++++++++++++++++- 2 files changed, 78 insertions(+), 3 deletions(-) diff --git a/backend/templates/error.ts b/backend/templates/error.ts index 28c7c4b..8297780 100644 --- a/backend/templates/error.ts +++ b/backend/templates/error.ts @@ -13,8 +13,56 @@ export class TemplateError extends Error { let format = ''; if (this.range) { - format += this.range.file.code + '\n'; - format += ' '.repeat(this.range.from) + '^' + '~'.repeat(Math.max(this.range.to - this.range.from, 0)) + '\n'; + let fcol = 0; + let fline = 1; + let pstart = 0; + + for (let i = 0; i < this.range.from; i++) { + fcol++; + if (this.range.file.code[i] == '\n') { + fline++; + fcol = 0; + pstart = i + 1; + } + } + + let tcol = fcol; + let tline = fline; + let pend = pstart; + + for (let i = this.range.from; i < this.range.to; i++) { + tcol++; + if (this.range.file.code[i] == '\n') { + tline++; + tcol = 0; + pend = i + 1; + } + } + + pend = Math.max(this.range.file.code.indexOf('\n', pend), pend); + + if (fline == tline) { + format += `${fline.toString().padStart(5, ' ')} | ${this.range.file.code.substring(pstart, pend)}\n`; + format += `${fline.toString().padStart(5, ' ')} | ${' '.repeat(fcol)}^${'~'.repeat(Math.max(tcol - fcol, 0))}\n`; + } else { + let lines = this.range.file.code.substring(pstart, pend).split('\n'); + + format += ` | /${'~'.repeat(lines[0].length)}v\n`; + + for (let i = fline; i < fline + 5 && i <= tline; i++) { + format += `${i.toString().padStart(5, ' ')} | | ${lines[i - fline]}\n`; + } + + if (fline + 5 < tline) { + format += ` | | ...\n`; + + for (let i = tline - 4; i <= tline; i++) { + format += `${i.toString().padStart(5, ' ')} | | ${lines[i - fline]}\n`; + } + } + + format += ` | \\${'~'.repeat(tcol + 1)}^\n`; + } } format += `${this.name}: ${this.message}`; diff --git a/backend/templates/parser.ts b/backend/templates/parser.ts index 50a714b..e8c3734 100644 --- a/backend/templates/parser.ts +++ b/backend/templates/parser.ts @@ -1,4 +1,7 @@ import { TemplateOp, TemplateSource } from 'ass'; + +import fs from 'fs'; + import { TemplateSyntaxError } from './error'; enum TokenType { @@ -97,6 +100,10 @@ function getTemplateTokens(src: string): TemplateToken[] { pos++; buf += findReplacement(); pos--; continue; + } else if (src[pos] == '\n') { + pos++; + for (; pos < src.length && (src[pos] == ' ' || src[pos] == '\t'); pos++); + pos--; continue; } else { buf += src[pos]; continue; @@ -203,7 +210,7 @@ export function prepareTemplate(src: string): TemplateOp { } else if (pos < tokens.length) { if (tokens[pos].type == TokenType.T_CLOSE) { throw new TemplateSyntaxError('Template name missing', { file: file, from: tokens[pos - 1].from, to: tokens[pos].to }); - } else throw new TemplateSyntaxError('Expected template name', { file: file, from: tokens[pos].from, to: tokens[pos].to }); + } else throw new TemplateSyntaxError('Expected template name', { file: file, from: tokens[pos].from, to: tokens[pos].to }); } else throw new TemplateSyntaxError('Unexpected end of file'); if (pos < tokens.length && tokens[pos].type == TokenType.PIPE) { @@ -253,6 +260,26 @@ export function prepareTemplate(src: string): TemplateOp { stackDrop(); + // include is executed early + if (name.toLowerCase() == 'include') { + if (nargs['file'] != null) { + // TODO: this NEEDS to be restricted before ass 0.15.0 is released + // its extremely insecure and should be restricted to things + // set by operators, users can have their own template inclusion + // thing that doesnt depend on reading files + + if (typeof nargs['file'] == 'string') { + if (fs.existsSync(nargs['file'])) { + let template = fs.readFileSync(nargs['file'], { encoding: 'utf-8' }); + + let tl = prepareTemplate(template); + + return tl; + } else throw new TemplateSyntaxError('File does not exist', { file: file, from: tokens[start].from, to: tokens[pos - 1].to}); + } else throw new TemplateSyntaxError('Include directive can not contain templates', { file: file, from: tokens[start].from, to: tokens[pos - 1].to}); + } else throw new TemplateSyntaxError(`Bad include directive`, { file: file, from: tokens[start].from, to: tokens[pos - 1].to}); + } + return { op: name.toLocaleLowerCase(), named: nargs,