lib/lexer.js (205 lines of code) (raw):

'use strict'; const { Lexer: BaseLexer, Token } = require('@jacksontian/skyline'); const { Tag } = require('./tag'); class AnnotationStart extends Token { constructor() { super(Tag.START); } toString() { return 'AnnotationStart: /**'; } } class AnnotationEnd extends Token { constructor() { super(Tag.END); } toString() { return 'AnnotationEnd: */'; } } class Text extends Token { constructor(text) { super(Tag.TEXT); this.text = text; } toString() { return `Text: ${this.text}`; } } class ID extends Token { constructor(id) { super(Tag.ID); this.id = id; } toString() { return `ID: ${this.id}`; } } /*********************************************************** * 功能: 判断c是否为字母(a-z,A-Z) * c: 字符值 **********************************************************/ function isLetter(c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); } class Type extends Token { constructor(typeName) { super(Tag.TYPE); this.typeName = typeName; } toString() { return `Type: ${this.typeName}`; } } class Lexer extends BaseLexer { constructor(source, filename, position) { super(source, filename); this.inParam = false; this.inPropertyMode = false; // [] 匹配中 this.inError = false; } scanID() { let id = ''; while (isLetter(this.peek) || this.peek === '_') { id += this.peek; this.getch(); } return new ID(id); } scan() { if(this.peek === ' ' || this.peek === '\t' || this.peek === '\n' || this.peek === '\r') { this.inTypeName = false; } this.skipWhitespaces(); if (this.inParam) { if (isLetter(this.peek) || this.peek === '_') { this.inParam = false; return this.scanID(); } throw new SyntaxError(`Unexpect token: '${this.peek}'`); } if (this.inError) { if (isLetter(this.peek) || this.peek === '_') { this.inError = false; return this.scanID(); } throw new SyntaxError(`Unexpect token: '${this.peek}'`); } if (this.inPropertyMode) { if (this.peek === ']') { this.inPropertyMode = false; this.getch(); return new Token(']'); } if (this.peek === '=') { this.getch(); return new Token('='); } if (this.peek === ',') { this.getch(); return new Token(','); } if (isLetter(this.peek) || this.peek === '_') { return this.scanID(); } throw new SyntaxError(`Unexpect token: '${this.peek}'`); } if (this.peek === '/') { this.getch(); var next0 = this.peek; this.getch(); var next1 = this.peek; if (next0 === '*' && next1 === '*') { this.getch(); return new AnnotationStart(); } this.ungetch(); this.ungetch(); } if (this.peek === '*') { this.getch(); if (this.peek === '/') { this.getch(); return new AnnotationEnd(); } if (this.peek === ' ') { this.getch(); } } if (this.inTypeName && this.peek === '[') { this.inPropertyMode = true; this.getch(); return new Token('['); } if (!this.peek) { var tok = new Token(this.peek); this.peek = ' '; return tok; } if (this.peek === '@') { let typeName = ''; this.getch(); while (this.peek !== ' ' && this.peek !== '\n' && this.peek !== '[') { typeName += this.peek; this.getch(); } if (typeName === 'param') { this.inParam = true; } if (typeName === 'error') { this.inError = true; } if(this.peek === '[') { this.inTypeName = true; } return new Type(typeName); } let str = `${this.peek}`; var lineEnd = false; for ( ; ; ) { this.getch(); if (lineEnd) { while (this.peek === ' ') { this.getch(); } if (this.peek === '*') { this.getch(); if (this.peek === ' ') { this.getch(); lineEnd = false; } else if (this.peek === '/') { this.ungetch(); this.ungetch(); break; } } if (this.peek === '@') { this.ungetch(); break; } } if (this.peek === '\n') { lineEnd = true; } if (this.peek === '*') { this.getch(); if (this.peek === '/') { this.ungetch(); break; } this.ungetch(); } if (this.peek) { str += this.peek; } else { throw new SyntaxError('Unexpect end of file'); } } return new Text(str); } } module.exports = Lexer;