diff --git a/base/src/com/google/idea/blaze/base/lang/buildfile/highlighting/BuildSyntaxHighlighter.java b/base/src/com/google/idea/blaze/base/lang/buildfile/highlighting/BuildSyntaxHighlighter.java index 093d6481696..c6a4557ccfb 100644 --- a/base/src/com/google/idea/blaze/base/lang/buildfile/highlighting/BuildSyntaxHighlighter.java +++ b/base/src/com/google/idea/blaze/base/lang/buildfile/highlighting/BuildSyntaxHighlighter.java @@ -75,6 +75,7 @@ private static TextAttributesKey key(String name, TextAttributesKey fallbackKey) static { addAttribute(TokenKind.COMMENT, BUILD_LINE_COMMENT); addAttribute(TokenKind.INT, BUILD_NUMBER); + addAttribute(TokenKind.FLOAT, BUILD_NUMBER); addAttribute(TokenKind.STRING, BUILD_STRING); addAttribute(TokenKind.LBRACE, BUILD_BRACES); diff --git a/base/src/com/google/idea/blaze/base/lang/buildfile/language/semantics/BuiltInNamesProvider.java b/base/src/com/google/idea/blaze/base/lang/buildfile/language/semantics/BuiltInNamesProvider.java index 5ce72f612a0..282af8af634 100644 --- a/base/src/com/google/idea/blaze/base/lang/buildfile/language/semantics/BuiltInNamesProvider.java +++ b/base/src/com/google/idea/blaze/base/lang/buildfile/language/semantics/BuiltInNamesProvider.java @@ -41,6 +41,7 @@ public class BuiltInNamesProvider { "enumerate", "fail", "False", + "float", "getattr", "hasattr", "hash", diff --git a/base/src/com/google/idea/blaze/base/lang/buildfile/lexer/BuildLexerBase.java b/base/src/com/google/idea/blaze/base/lang/buildfile/lexer/BuildLexerBase.java index 1cbe57dcbc6..d05868ab008 100644 --- a/base/src/com/google/idea/blaze/base/lang/buildfile/lexer/BuildLexerBase.java +++ b/base/src/com/google/idea/blaze/base/lang/buildfile/lexer/BuildLexerBase.java @@ -19,6 +19,7 @@ import com.google.common.collect.Lists; import java.util.List; import java.util.Stack; +import java.util.function.Predicate; import javax.annotation.Nullable; /** @@ -559,8 +560,32 @@ private void addIdentifierOrKeyword() { addToken(kind, oldPos, pos, (kind == TokenKind.IDENTIFIER) ? id : null); } + private String scanDecimal() { + int oldPos = pos; + while (pos < buffer.length) { + char c = buffer[pos]; + switch (c) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + pos++; + break; + default: + return bufferSlice(oldPos, pos); + } + } + return bufferSlice(oldPos, pos); + } + private String scanInteger() { - int oldPos = pos - 1; + int oldPos = pos; while (pos < buffer.length) { char c = buffer[pos]; switch (c) { @@ -600,17 +625,21 @@ private String scanInteger() { } /** - * Scans an addInteger literal. + * Scans an integer or float literal. * - *

ON ENTRY: 'pos' is 1 + the index of the first char in the literal. ON EXIT: 'pos' is 1 + the + *

ON ENTRY: 'pos' is the index of the first char in the literal. ON EXIT: 'pos' is 1 + the * index of the last char in the literal. */ - private void addInteger() { - int oldPos = pos - 1; + private void addNumber() { + // https://github.com/bazelbuild/starlark/blob/master/spec.md#lexical-elements + + int oldPos = pos; String literal = scanInteger(); - final String substring; - final int radix; + String substring; + int radix; + boolean isFloat = false; + boolean hasErrors = false; if (literal.startsWith("0x") || literal.startsWith("0X")) { radix = 16; substring = literal.substring(2); @@ -626,14 +655,50 @@ private void addInteger() { substring = literal; } - int value = 0; - try { - value = Integer.parseInt(substring, radix); - } catch (NumberFormatException e) { - error("invalid base-" + radix + " integer constant: " + literal); + if (radix == 10 && lookaheadIs(0, '.')) { + pos++; + isFloat = true; + scanDecimal(); + literal = bufferSlice(oldPos, pos); + substring = literal; + } + if (radix == 10 && (lookaheadIs(0, 'e') || lookaheadIs(0, 'E'))) { + pos++; + isFloat = true; + if (lookaheadIs(0, '+') || lookaheadIs(0, '-')) { + pos++; + } + String exp = scanDecimal(); + if (exp.isEmpty()) { + hasErrors = true; + } + literal = bufferSlice(oldPos, pos); + substring = literal; } - addToken(TokenKind.INT, oldPos, pos, value); + if (isFloat) { + double value = 0; + try { + value = Double.parseDouble(substring); + } catch (NumberFormatException e) { + hasErrors = true; + } + if (hasErrors) { + error("invalid float constant: " + literal, oldPos, pos); + } + addToken(TokenKind.FLOAT, oldPos, pos, value); + } else { + int value = 0; + try { + value = Integer.parseInt(substring, radix); + } catch (NumberFormatException e) { + hasErrors = true; + } + if (hasErrors) { + error("invalid base-" + radix + " integer constant: " + literal); + } + addToken(TokenKind.INT, oldPos, pos, value); + } } /** @@ -665,6 +730,10 @@ private boolean lookaheadIs(int p, char c) { return pos + p < buffer.length && buffer[pos + p] == c; } + private boolean lookaheadMatches(int p, Predicate pred) { + return pos + p < buffer.length && pred.test(buffer[pos + p]); + } + /** Performs tokenization of the character buffer of file contents provided to the constructor. */ private void tokenize() { while (pos < buffer.length) { @@ -741,9 +810,6 @@ private void tokenize() { case ';': addToken(TokenKind.SEMI, pos - 1, pos); break; - case '.': - addToken(TokenKind.DOT, pos - 1, pos); - break; case '*': addToken(TokenKind.STAR, pos - 1, pos); break; @@ -791,8 +857,12 @@ private void tokenize() { break; } - if (Character.isDigit(c)) { - addInteger(); + // Distinguish dot vs. start of a float. + if (c == '.' && !lookaheadMatches(0, Character::isDigit)) { + addToken(TokenKind.DOT, pos - 1, pos); + } else if (Character.isDigit(c) || c == '.') { + pos--; + addNumber(); } else if (Character.isJavaIdentifierStart(c) && c != '$') { addIdentifierOrKeyword(); } else { diff --git a/base/src/com/google/idea/blaze/base/lang/buildfile/lexer/TokenKind.java b/base/src/com/google/idea/blaze/base/lang/buildfile/lexer/TokenKind.java index d6b48245b82..8071984cc84 100644 --- a/base/src/com/google/idea/blaze/base/lang/buildfile/lexer/TokenKind.java +++ b/base/src/com/google/idea/blaze/base/lang/buildfile/lexer/TokenKind.java @@ -38,6 +38,7 @@ public enum TokenKind { EQUALS_EQUALS("=="), EXCEPT("except"), FINALLY("finally"), + FLOAT("float"), FOR("for"), FROM("from"), GLOBAL("global"), diff --git a/base/src/com/google/idea/blaze/base/lang/buildfile/parser/ExpressionParsing.java b/base/src/com/google/idea/blaze/base/lang/buildfile/parser/ExpressionParsing.java index 295a27b7d2d..40276754cb6 100644 --- a/base/src/com/google/idea/blaze/base/lang/buildfile/parser/ExpressionParsing.java +++ b/base/src/com/google/idea/blaze/base/lang/buildfile/parser/ExpressionParsing.java @@ -249,6 +249,9 @@ private void parsePrimary() { case INT: buildTokenElement(BuildElementTypes.INTEGER_LITERAL); return; + case FLOAT: + buildTokenElement(BuildElementTypes.FLOAT_LITERAL); + return; case STRING: parseStringLiteral(true); return; diff --git a/base/src/com/google/idea/blaze/base/lang/buildfile/psi/BuildElementTypes.java b/base/src/com/google/idea/blaze/base/lang/buildfile/psi/BuildElementTypes.java index 7c4d1428b42..17498147614 100644 --- a/base/src/com/google/idea/blaze/base/lang/buildfile/psi/BuildElementTypes.java +++ b/base/src/com/google/idea/blaze/base/lang/buildfile/psi/BuildElementTypes.java @@ -71,6 +71,7 @@ public interface BuildElementTypes { BuildElementType DOT_EXPRESSION = new BuildElementType("dot_expr", DotExpression.class); BuildElementType STRING_LITERAL = new BuildElementType("string", StringLiteral.class); BuildElementType INTEGER_LITERAL = new BuildElementType("int", IntegerLiteral.class); + BuildElementType FLOAT_LITERAL = new BuildElementType("float", FloatLiteral.class); BuildElementType LIST_LITERAL = new BuildElementType("list", ListLiteral.class); BuildElementType GLOB_EXPRESSION = new BuildElementType("glob", GlobExpression.class); BuildElementType REFERENCE_EXPRESSION = @@ -92,6 +93,7 @@ public interface BuildElementTypes { DOT_EXPRESSION, STRING_LITERAL, INTEGER_LITERAL, + FLOAT_LITERAL, LIST_LITERAL, PARENTHESIZED_EXPRESSION, TUPLE_EXPRESSION, diff --git a/base/src/com/google/idea/blaze/base/lang/buildfile/psi/BuildElementVisitor.java b/base/src/com/google/idea/blaze/base/lang/buildfile/psi/BuildElementVisitor.java index 804a5dc8c61..fcbafeefac1 100644 --- a/base/src/com/google/idea/blaze/base/lang/buildfile/psi/BuildElementVisitor.java +++ b/base/src/com/google/idea/blaze/base/lang/buildfile/psi/BuildElementVisitor.java @@ -104,6 +104,10 @@ public void visitIntegerLiteral(IntegerLiteral node) { visitElement(node); } + public void visitFloatLiteral(FloatLiteral node) { + visitElement(node); + } + public void visitListLiteral(ListLiteral node) { visitElement(node); } diff --git a/base/src/com/google/idea/blaze/base/lang/buildfile/psi/FloatLiteral.java b/base/src/com/google/idea/blaze/base/lang/buildfile/psi/FloatLiteral.java new file mode 100644 index 00000000000..bbd03744546 --- /dev/null +++ b/base/src/com/google/idea/blaze/base/lang/buildfile/psi/FloatLiteral.java @@ -0,0 +1,15 @@ +package com.google.idea.blaze.base.lang.buildfile.psi; + +import com.intellij.lang.ASTNode; + +public class FloatLiteral extends BuildElementImpl implements LiteralExpression { + + public FloatLiteral(ASTNode astNode) { + super(astNode); + } + + @Override + protected void acceptVisitor(BuildElementVisitor visitor) { + visitor.visitFloatLiteral(this); + } +} diff --git a/base/tests/unittests/com/google/idea/blaze/base/lang/buildfile/lexer/BlazeLexerTest.java b/base/tests/unittests/com/google/idea/blaze/base/lang/buildfile/lexer/BlazeLexerTest.java index dcb3db1c276..7ab1e75a9cd 100644 --- a/base/tests/unittests/com/google/idea/blaze/base/lang/buildfile/lexer/BlazeLexerTest.java +++ b/base/tests/unittests/com/google/idea/blaze/base/lang/buildfile/lexer/BlazeLexerTest.java @@ -63,28 +63,29 @@ public void testBasics4() throws Exception { assertEquals("", names(tokens(""))); assertEquals("", names(tokens("# foo"))); assertEquals("INT INT INT INT", names(tokens("1 2 3 4"))); - assertEquals("INT DOT INT", names(tokens("1.234"))); + assertEquals("FLOAT", names(tokens("1.234"))); assertEquals( "IDENTIFIER LPAREN IDENTIFIER COMMA IDENTIFIER RPAREN", names(tokens("foo(bar, wiz)"))); } @Test public void testIntegersAndDot() throws Exception { - assertEquals("INT(1) DOT INT(2345)", values(tokens("1.2345"))); + assertEquals("FLOAT(1.2345)", values(tokens("1.2345"))); - assertEquals("INT(1) DOT INT(2) DOT INT(345)", values(tokens("1.2.345"))); + assertEquals("FLOAT(1.2) FLOAT(0.345)", values(tokens("1.2.345"))); - assertEquals("INT(1) DOT INT(0)", values(tokens("1.23E10"))); - assertEquals("invalid base-10 integer constant: 23E10", lastError); + assertEquals("FLOAT(1.23E10)", values(tokens("1.23E10"))); - assertEquals("INT(1) DOT INT(0) MINUS INT(10)", values(tokens("1.23E-10"))); - assertEquals("invalid base-10 integer constant: 23E", lastError); + assertEquals("FLOAT(0.0)", values(tokens("1.23E"))); + assertEquals("invalid float constant: 1.23E", lastError); + assertEquals("FLOAT(1.23E-10)", values(tokens("1.23E-10"))); assertEquals("DOT INT(123)", values(tokens(". 123"))); - assertEquals("DOT INT(123)", values(tokens(".123"))); + assertEquals("FLOAT(0.123)", values(tokens(".123"))); assertEquals("DOT IDENTIFIER(abc)", values(tokens(".abc"))); - assertEquals("IDENTIFIER(foo) DOT INT(123)", values(tokens("foo.123"))); + assertEquals("IDENTIFIER(foo) FLOAT(0.123)", values(tokens("foo.123"))); + assertEquals("IDENTIFIER(foo456) FLOAT(0.123)", values(tokens("foo456.123"))); assertEquals( "IDENTIFIER(foo) DOT IDENTIFIER(bcd)", values(tokens("foo.bcd"))); // 'b' are hex chars assertEquals("IDENTIFIER(foo) DOT IDENTIFIER(xyz)", values(tokens("foo.xyz"))); diff --git a/base/tests/unittests/com/google/idea/blaze/base/lang/buildfile/lexer/HighlightingLexerTest.java b/base/tests/unittests/com/google/idea/blaze/base/lang/buildfile/lexer/HighlightingLexerTest.java index 800d6cf660c..50ecc65de02 100644 --- a/base/tests/unittests/com/google/idea/blaze/base/lang/buildfile/lexer/HighlightingLexerTest.java +++ b/base/tests/unittests/com/google/idea/blaze/base/lang/buildfile/lexer/HighlightingLexerTest.java @@ -63,7 +63,8 @@ public void testBasics4() throws Exception { assertEquals("", names(tokens(""))); assertEquals("COMMENT", names(tokens("# foo"))); assertEquals("INT WHITESPACE INT WHITESPACE INT WHITESPACE INT", names(tokens("1 2 3 4"))); - assertEquals("INT DOT INT", names(tokens("1.234"))); + assertEquals("FLOAT", names(tokens("1.234"))); + assertEquals("DOT IDENTIFIER", names(tokens(".a"))); assertEquals( "IDENTIFIER LPAREN IDENTIFIER COMMA WHITESPACE IDENTIFIER RPAREN", names(tokens("foo(bar, wiz)"))); @@ -71,21 +72,22 @@ public void testBasics4() throws Exception { @Test public void testIntegersAndDot() throws Exception { - assertEquals("INT(1) DOT INT(2345)", values(tokens("1.2345"))); + assertEquals("FLOAT(1.2345)", values(tokens("1.2345"))); - assertEquals("INT(1) DOT INT(2) DOT INT(345)", values(tokens("1.2.345"))); + assertEquals("FLOAT(1.2) FLOAT(0.345)", values(tokens("1.2.345"))); - assertEquals("INT(1) DOT INT(0)", values(tokens("1.23E10"))); - assertEquals("invalid base-10 integer constant: 23E10", lastError); + assertEquals("FLOAT(1.23E10)", values(tokens("1.23E10"))); - assertEquals("INT(1) DOT INT(0) MINUS INT(10)", values(tokens("1.23E-10"))); - assertEquals("invalid base-10 integer constant: 23E", lastError); + assertEquals("FLOAT(0.0)", values(tokens("1.23E"))); + assertEquals("invalid float constant: 1.23E", lastError); + assertEquals("FLOAT(1.23E-10)", values(tokens("1.23E-10"))); assertEquals("DOT WHITESPACE INT(123)", values(tokens(". 123"))); - assertEquals("DOT INT(123)", values(tokens(".123"))); + assertEquals("FLOAT(0.123)", values(tokens(".123"))); assertEquals("DOT IDENTIFIER(abc)", values(tokens(".abc"))); - assertEquals("IDENTIFIER(foo) DOT INT(123)", values(tokens("foo.123"))); + assertEquals("IDENTIFIER(foo) FLOAT(0.123)", values(tokens("foo.123"))); + assertEquals("IDENTIFIER(foo456) FLOAT(0.123)", values(tokens("foo456.123"))); assertEquals( "IDENTIFIER(foo) DOT IDENTIFIER(bcd)", values(tokens("foo.bcd"))); // 'b' are hex chars assertEquals("IDENTIFIER(foo) DOT IDENTIFIER(xyz)", values(tokens("foo.xyz"))); diff --git a/skylark/src/com/google/idea/blaze/skylark/debugger/impl/SkylarkDebugValue.java b/skylark/src/com/google/idea/blaze/skylark/debugger/impl/SkylarkDebugValue.java index eea4d53adb8..6576eebf23c 100644 --- a/skylark/src/com/google/idea/blaze/skylark/debugger/impl/SkylarkDebugValue.java +++ b/skylark/src/com/google/idea/blaze/skylark/debugger/impl/SkylarkDebugValue.java @@ -61,7 +61,7 @@ static Type parseType(StarlarkDebuggingProtos.Value value) { // TODO(brendandouglas): move this logic onto the server side? private static final ImmutableSet ARRAY_TYPES = ImmutableSet.of("dict", "list", "depset"); private static final ImmutableSet PRIMITIVE_TYPES = - ImmutableSet.of("bool", "string", "int"); + ImmutableSet.of("bool", "string", "int", "float"); private static final ImmutableSet FUNCTION_TYPES = ImmutableSet.of("function", "Provider");