Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FS-1142] Extended numeric literal #17242

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 25 additions & 5 deletions src/Compiler/Checking/CheckExpressions.fs
Original file line number Diff line number Diff line change
Expand Up @@ -7593,18 +7593,38 @@ and TcConstExpr cenv (overallTy: OverallTy) env m tpenv c =
| Result []
| Exception _ -> error(Error(FSComp.SR.tcNumericLiteralRequiresModule modName, m))
| Result ((_, mref, _) :: _) ->

let mkFunctionCall (name, constExpr) =
SynExpr.App (ExprAtomicFlag.Atomic, false, mkSynLidGet m [modName] name, SynExpr.Const (constExpr, m), m)

let expr =
try
match int32 s with
| 0 -> SynExpr.App (ExprAtomicFlag.Atomic, false, mkSynLidGet m [modName] "FromZero", SynExpr.Const (SynConst.Unit, m), m)
| 1 -> SynExpr.App (ExprAtomicFlag.Atomic, false, mkSynLidGet m [modName] "FromOne", SynExpr.Const (SynConst.Unit, m), m)
| i32 -> SynExpr.App (ExprAtomicFlag.Atomic, false, mkSynLidGet m [modName] "FromInt32", SynExpr.Const (SynConst.Int32 i32, m), m)
| 0 -> mkFunctionCall ("FromZero", SynConst.Unit)
| 1 -> mkFunctionCall ("FromOne", SynConst.Unit)
| i32 -> mkFunctionCall ("FromInt32", SynConst.Int32 i32)
with _ ->
try
let i64 = int64 s
SynExpr.App (ExprAtomicFlag.Atomic, false, mkSynLidGet m [modName] "FromInt64", SynExpr.Const (SynConst.Int64 i64, m), m)
mkFunctionCall ("FromInt64", SynConst.Int64 i64)
with _ ->
SynExpr.App (ExprAtomicFlag.Atomic, false, mkSynLidGet m [modName] "FromString", SynExpr.Const (SynConst.String (s, SynStringKind.Regular, m), m), m)
try
let res = Regex.Match(s, @"^-?0*(?<number>\d+\.?\d*?)0*(?:$|[eE][+-]?(?<exp>\d+))")
let exp = res.Groups.["exp"]
let number = res.Groups.["number"]
let isNumberContainsDot = -1 <> number.Value.IndexOf '.'
let maxLen = if isNumberContainsDot then 16 else 15

if not res.Success || (not isNumberContainsDot && not exp.Success) then
mkFunctionCall ("FromString", SynConst.String (s, SynStringKind.Regular, m))
elif (not exp.Success || int exp.Value <= 300) && number.Length <= maxLen then
let f64 = float s
mkFunctionCall ("FromFloat", SynConst.Double f64)
else
mkFunctionCall ("FromFloatString", SynConst.String (s, SynStringKind.Regular, m))

with _ ->
mkFunctionCall ("FromString", SynConst.String (s, SynStringKind.Regular, m))

if suffix <> "I" then
expr
Expand Down
5 changes: 3 additions & 2 deletions src/Compiler/FSComp.txt
Original file line number Diff line number Diff line change
Expand Up @@ -636,7 +636,7 @@ tcExpressionWithIfRequiresParenthesis,"This list or array expression includes an
781,tcConstructorRequiresArguments,"This object constructor requires arguments"
782,tcNewRequiresObjectConstructor,"'new' may only be used with object constructors"
783,tcAtLeastOneOverrideIsInvalid,"At least one override did not correctly implement its corresponding abstract member"
784,tcNumericLiteralRequiresModule,"This numeric literal requires that a module '%s' defining functions FromZero, FromOne, FromInt32, FromInt64 and FromString be in scope"
784,tcNumericLiteralRequiresModule,"This numeric literal requires that a module '%s' defining functions FromZero, FromOne, FromInt32, FromInt64, FromString, FromFloat and FromFloatString be in scope"
785,tcInvalidRecordConstruction,"Invalid record construction"
786,tcExpressionFormRequiresRecordTypes,"The expression form {{ expr with ... }} may only be used with record types. To build object types use {{ new Type(...) with ... }}"
787,tcInheritedTypeIsNotObjectModelType,"The inherited type is not an object model type"
Expand Down Expand Up @@ -1035,7 +1035,7 @@ lexUnexpectedChar,"Unexpected character '%s'"
1153,lexInvalidFloat,"Invalid floating point number"
1154,lexOutsideDecimal,"This number is outside the allowable range for decimal literals"
1155,lexOutsideThirtyTwoBitFloat,"This number is outside the allowable range for 32-bit floats"
1156,lexInvalidNumericLiteral,"This is not a valid numeric literal. Valid numeric literals include 1, 0x1, 0o1, 0b1, 1l (int/int32), 1u (uint/uint32), 1L (int64), 1UL (uint64), 1s (int16), 1us (uint16), 1y (int8/sbyte), 1uy (uint8/byte), 1.0 (float/double), 1.0f (float32/single), 1.0m (decimal), 1I (bigint)."
1156,lexInvalidNumericLiteral,"This is not a valid numeric literal. Valid numeric literals include 1, 0x1, 0o1, 0b1, 1l (int/int32), 1u (uint/uint32), 1L (int64), 1UL (uint64), 1s (int16), 1us (uint16), 1y (int8/sbyte), 1uy (uint8/byte), 1.0 (float/double), 1.0f (float32/single), 1.0m (decimal), 1n (nativeint), 1un (unativeint), 1I (bigint)."
1157,lexInvalidByteLiteral,"This is not a valid byte literal"
1158,lexInvalidCharLiteral,"This is not a valid character literal"
1159,lexThisUnicodeOnlyInStringLiterals,"This Unicode encoding is only valid in string literals"
Expand Down Expand Up @@ -1597,6 +1597,7 @@ featureEnforceAttributeTargets,"Enforce AttributeTargets"
featureLowerInterpolatedStringToConcat,"Optimizes interpolated strings in certain cases, by lowering to concatenation"
featureLowerIntegralRangesToFastLoops,"Optimizes certain uses of the integral range (..) and range-step (.. ..) operators to fast while-loops."
featureLowerSimpleMappingsInComprehensionsToDirectCallsToMap,"Lowers [for x in xs -> f x] and [|for x in xs -> f x|] to calls to List.map and Array.map when xs is a list or an array, respectively."
featureExtendedNumericLiteral,"Underscores in numeric literal after prefix or before suffix and Hexadecimal, octal, binary and floating custom numeric literal"
3354,tcNotAFunctionButIndexerNamedIndexingNotYetEnabled,"This value supports indexing, e.g. '%s.[index]'. The syntax '%s[index]' requires /langversion:preview. See https://aka.ms/fsharp-index-notation."
3354,tcNotAFunctionButIndexerIndexingNotYetEnabled,"This expression supports indexing, e.g. 'expr.[index]'. The syntax 'expr[index]' requires /langversion:preview. See https://aka.ms/fsharp-index-notation."
3355,tcNotAnIndexerNamedIndexingNotYetEnabled,"The value '%s' is not a function and does not support index notation."
Expand Down
3 changes: 3 additions & 0 deletions src/Compiler/Facilities/LanguageFeatures.fs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ type LanguageFeature =
| LowerInterpolatedStringToConcat
| LowerIntegralRangesToFastLoops
| LowerSimpleMappingsInComprehensionsToDirectCallsToMap
| ExtendedNumericLiteral

/// LanguageVersion management
type LanguageVersion(versionText) =
Expand Down Expand Up @@ -205,6 +206,7 @@ type LanguageVersion(versionText) =
LanguageFeature.LowerInterpolatedStringToConcat, previewVersion
LanguageFeature.LowerIntegralRangesToFastLoops, previewVersion
LanguageFeature.LowerSimpleMappingsInComprehensionsToDirectCallsToMap, previewVersion
LanguageFeature.ExtendedNumericLiteral, previewVersion
]

static let defaultLanguageVersion = LanguageVersion("default")
Expand Down Expand Up @@ -353,6 +355,7 @@ type LanguageVersion(versionText) =
| LanguageFeature.LowerIntegralRangesToFastLoops -> FSComp.SR.featureLowerIntegralRangesToFastLoops ()
| LanguageFeature.LowerSimpleMappingsInComprehensionsToDirectCallsToMap ->
FSComp.SR.featureLowerSimpleMappingsInComprehensionsToDirectCallsToMap ()
| LanguageFeature.ExtendedNumericLiteral -> FSComp.SR.featureExtendedNumericLiteral ()

/// Get a version string associated with the given feature.
static member GetFeatureVersionString feature =
Expand Down
1 change: 1 addition & 0 deletions src/Compiler/Facilities/LanguageFeatures.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ type LanguageFeature =
| LowerInterpolatedStringToConcat
| LowerIntegralRangesToFastLoops
| LowerSimpleMappingsInComprehensionsToDirectCallsToMap
| ExtendedNumericLiteral

/// LanguageVersion management
type LanguageVersion =
Expand Down
16 changes: 16 additions & 0 deletions src/Compiler/SyntaxTree/LexHelpers.fs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ module internal FSharp.Compiler.Lexhelp

open System
open System.Text
open System.Text.RegularExpressions

open Internal.Utilities
open Internal.Utilities.Library
Expand Down Expand Up @@ -303,6 +304,21 @@ let escape c =
| 'r' -> '\r'
| c -> c

let failsWhenExtendedNumericLiteralNotAvailable (langVersion: LanguageVersion) (s: string) (m: range) =
let isSeperatorInNewPlace =
Regex.IsMatch(s, "^[+-]?0[xob]_")
|| Regex.IsMatch(s, "_[uU]?[luLsyfmMnINZQRG]$")

let isExtendedUserNum s =
let isXInteger = Regex.IsMatch(s, "^[+-]?0[xob]")
let isFloat = Regex.IsMatch(s, @"[eE\.]")
let isUserNum = Regex.IsMatch(s, "[INZQRG]$")

isUserNum && (isXInteger || isFloat)

if isSeperatorInNewPlace || isExtendedUserNum s then
checkLanguageFeatureAndRecover langVersion LanguageFeature.ExtendedNumericLiteral m

//------------------------------------------------------------------------
// Keyword table
//-----------------------------------------------------------------------
Expand Down
4 changes: 4 additions & 0 deletions src/Compiler/SyntaxTree/LexHelpers.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,10 @@ val unicodeGraphLong: string -> LongUnicodeLexResult

val escape: char -> char

/// fails when the input is a extended numeric literal (0x_1 or 0x1_l or 0x1I or 1.0I) if
/// the language of the lang version does not support ExtendedNumericLiteral
val failsWhenExtendedNumericLiteralNotAvailable: Features.LanguageVersion -> string -> range -> unit

exception ReservedKeyword of string * range

module Keywords =
Expand Down
Loading
Loading