Skip to content
Open
45 changes: 32 additions & 13 deletions src/parser/scanner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,10 @@ export class Scanner implements ITokenStream {
}
case '.': {
this.stream.next();
if (!this.stream.eof && digit.test(this.stream.char as string)) {
const digitToken = this.tryReadDigits(hasLeftSpacing, pos, true);
if (digitToken) return digitToken;
}
return TOKEN(TokenKind.Dot, pos, { hasLeftSpacing });
}
case '/': {
Expand Down Expand Up @@ -312,7 +316,7 @@ export class Scanner implements ITokenStream {
return TOKEN(TokenKind.CloseBrace, pos, { hasLeftSpacing });
}
default: {
const digitToken = this.tryReadDigits(hasLeftSpacing);
const digitToken = this.tryReadDigits(hasLeftSpacing, pos, false);
if (digitToken) return digitToken;

const wordToken = this.tryReadWord(hasLeftSpacing);
Expand Down Expand Up @@ -413,26 +417,30 @@ export class Scanner implements ITokenStream {
}
}

private tryReadDigits(hasLeftSpacing: boolean): Token | undefined {
private tryReadDigits(
hasLeftSpacing: boolean,
pos: { line: number, column: number },
hasLeadingDot: boolean,
): Token | undefined {
let wholeNumber = '';
let fractional = '';

const pos = this.stream.getPos();

while (!this.stream.eof && digit.test(this.stream.char)) {
wholeNumber += this.stream.char;
this.stream.next();
}
if (wholeNumber.length === 0) {
return;
if (!hasLeadingDot) {
while (!this.stream.eof && digit.test(this.stream.char)) {
wholeNumber += this.stream.char;
this.stream.next();
}
if (wholeNumber.length === 0) {
return;
}
}
if (!this.stream.eof && this.stream.char === '.') {
this.stream.next();
const decimalPoint = this.tryReadDecimalPoint(hasLeadingDot);
if (decimalPoint) {
while (!this.stream.eof as boolean && digit.test(this.stream.char as string)) {
fractional += this.stream.char;
this.stream.next();
}
if (fractional.length === 0) {
if (wholeNumber.length === 0 && fractional.length === 0) {
throw new AiScriptSyntaxError('digit expected', pos);
}
}
Expand All @@ -445,6 +453,17 @@ export class Scanner implements ITokenStream {
return TOKEN(TokenKind.NumberLiteral, pos, { hasLeftSpacing, value });
}

private tryReadDecimalPoint(hasLeadingDot: boolean): boolean {
if (hasLeadingDot) {
return true;
}
if (!this.stream.eof && this.stream.char === '.') {
this.stream.next();
return true;
}
return false;
}

private readStringLiteral(hasLeftSpacing: boolean): Token {
let value = '';
const literalMark = this.stream.char;
Expand Down
14 changes: 14 additions & 0 deletions test/literals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,20 @@ describe('literal', () => {
eq(res, NUM(0.5));
});

test.concurrent('number (Float with integer zero omitted)', async () => {
const res = await exe(`
<: .5
`);
eq(res, NUM(0.5));
});

test.concurrent('number (Float with decimal zero omitted)', async () => {
const res = await exe(`
<: 5.
`);
eq(res, NUM(5));
});

test.concurrent('arr (separated by comma)', async () => {
const res = await exe(`
<: [1, 2, 3]
Expand Down
3 changes: 3 additions & 0 deletions unreleased/zero-part-ommitable-decimal-literal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
- 数値リテラルの整数部分または小数部分の0を省略できるようになりました。
- 整数でない数値リテラルは、整数部分が0の場合、0を省略して記述できるようになりました。
- 整数の数値リテラルは、小数部分の0を省略して記述できるようになりました。
Loading