diff --git a/src/parser/scanner.ts b/src/parser/scanner.ts index cb86984f9..ac55843fa 100644 --- a/src/parser/scanner.ts +++ b/src/parser/scanner.ts @@ -10,6 +10,7 @@ const spaceChars = [' ', '\t']; const lineBreakChars = ['\r', '\n']; const digit = /^[0-9]$/; const wordChar = /^[A-Za-z0-9_]$/; +const exponentIndicatorPattern = /^[eE]$/; /** * 入力文字列からトークンを読み取るクラス @@ -436,12 +437,38 @@ export class Scanner implements ITokenStream { throw new AiScriptSyntaxError('digit expected', pos); } } + + let exponentIndicator = ''; + let exponentSign = ''; + let exponentAbsolute = ''; + if (!this.stream.eof && exponentIndicatorPattern.test(this.stream.char as string)) { + exponentIndicator = this.stream.char as string; + this.stream.next(); + if (!this.stream.eof && (this.stream.char as string) === '-') { + exponentSign = '-'; + this.stream.next(); + } else if (!this.stream.eof && (this.stream.char as string) === '+') { + exponentSign = '+'; + this.stream.next(); + } + while (!this.stream.eof && digit.test(this.stream.char)) { + exponentAbsolute += this.stream.char; + this.stream.next(); + } + if (exponentAbsolute.length === 0) { + throw new AiScriptSyntaxError('exponent expected', pos); + } + } + let value: string; if (fractional.length > 0) { value = wholeNumber + '.' + fractional; } else { value = wholeNumber; } + if (exponentIndicator.length > 0) { + value += exponentIndicator + exponentSign + exponentAbsolute; + } return TOKEN(TokenKind.NumberLiteral, pos, { hasLeftSpacing, value }); } diff --git a/test/literals.ts b/test/literals.ts index 2167c485f..f52187b55 100644 --- a/test/literals.ts +++ b/test/literals.ts @@ -58,6 +58,33 @@ describe('literal', () => { eq(res, NUM(0.5)); }); + test.concurrent('number (positive exponent without plus sign)', async () => { + const res = await exe(` + <: 1.2e3 + `); + eq(res, NUM(1200)); + }); + + test.concurrent('number (positive exponent with plus sign)', async () => { + const res = await exe(` + <: 1.2e+3 + `); + eq(res, NUM(1200)); + }); + + test.concurrent('number (negative exponent)', async () => { + const res = await exe(` + <: 1.2e-3 + `); + eq(res, NUM(0.0012)); + }); + + test.concurrent('number (missing exponent)', async () => { + assert.rejects(() => exe(` + <: 1.2e+ + `), 'exponent expected'); + }); + test.concurrent('arr (separated by comma)', async () => { const res = await exe(` <: [1, 2, 3] diff --git a/unreleased/exponential-notation.md b/unreleased/exponential-notation.md new file mode 100644 index 000000000..15fb92137 --- /dev/null +++ b/unreleased/exponential-notation.md @@ -0,0 +1 @@ +- 数値リテラルを指数表記で記述できるようになりました。