code frames

pull/31/head
Rich-Harris 8 years ago
parent 43c257b62e
commit a08cb44377

@ -2,6 +2,41 @@ import { locate } from 'locate-character';
import fragment from './state/fragment.js'; import fragment from './state/fragment.js';
import { whitespace } from './patterns.js'; import { whitespace } from './patterns.js';
import { trimStart, trimEnd } from './utils/trim.js'; import { trimStart, trimEnd } from './utils/trim.js';
import spaces from './utils/spaces.js';
function tabsToSpaces ( str ) {
return str.replace( /^\t+/, match => match.split( '\t' ).join( ' ' ) );
}
function ParseError ( message, template, index ) {
const { line, column } = locate( template, index );
const lines = template.split( '\n' );
const frameStart = Math.max( 0, line - 2 );
const frameEnd = Math.min( line + 3, lines.length );
const digits = String( frameEnd + 1 ).length;
const frame = lines
.slice( frameStart, frameEnd )
.map( ( str, i ) => {
const isErrorLine = frameStart + i === line;
let lineNum = String( i + frameStart + 1 );
while ( lineNum.length < digits ) lineNum = ` ${lineNum}`;
if ( isErrorLine ) {
const indicator = spaces( digits + 2 + tabsToSpaces( str.slice( 0, column ) ).length ) + '^';
return `${lineNum}: ${tabsToSpaces( str )}\n${indicator}`;
}
return `${lineNum}: ${tabsToSpaces( str )}`;
})
.join( '\n' );
this.message = `${message} (${line + 1}:${column})\n${frame}`;
this.loc = { line, column };
this.shortMessage = message;
}
export default function parse ( template ) { export default function parse ( template ) {
const parser = { const parser = {
@ -13,9 +48,12 @@ export default function parse ( template ) {
return this.stack[ this.stack.length - 1 ]; return this.stack[ this.stack.length - 1 ];
}, },
acornError ( err ) {
parser.error( err.message.replace( /\(\d+:\d+\)$/, '' ), err.pos );
},
error ( message, index = this.index ) { error ( message, index = this.index ) {
const { line, column } = locate( this.template, index ); throw new ParseError( message, this.template, index );
throw new Error( `${message} (${line}:${column})` );
}, },
eat ( str, required ) { eat ( str, required ) {

@ -1,11 +1,15 @@
import { parseExpressionAt } from 'acorn'; import { parseExpressionAt } from 'acorn';
export default function readExpression ( parser ) { export default function readExpression ( parser ) {
const node = parseExpressionAt( parser.template, parser.index ); try {
parser.index = node.end; const node = parseExpressionAt( parser.template, parser.index );
parser.index = node.end;
// TODO check it's a valid expression. probably shouldn't have // TODO check it's a valid expression. probably shouldn't have
// [arrow] function expressions, etc // [arrow] function expressions, etc
return node; return node;
} catch ( err ) {
parser.acornError( err );
}
} }

@ -1,10 +1,5 @@
import { parse, tokenizer } from 'acorn'; import { parse, tokenizer } from 'acorn';
import spaces from '../utils/spaces.js';
function spaces ( i ) {
let result = '';
while ( i-- ) result += ' ';
return result;
}
export default function readScript ( parser, start, attributes ) { export default function readScript ( parser, start, attributes ) {
const scriptStart = parser.index; const scriptStart = parser.index;

@ -0,0 +1,5 @@
export default function spaces ( i ) {
let result = '';
while ( i-- ) result += ' ';
return result;
}
Loading…
Cancel
Save