diff --git a/backend/src/BuiltinExecution/Libs/Int64.fs b/backend/src/BuiltinExecution/Libs/Int64.fs index c26bb95382..8ac0239ef2 100644 --- a/backend/src/BuiltinExecution/Libs/Int64.fs +++ b/backend/src/BuiltinExecution/Libs/Int64.fs @@ -67,11 +67,11 @@ let fns : List = // See above for when to uncomment this // TODO: A future version should support all non-zero modulus values and should include the infix "%" // { name = fn "mod" 0 - // parameters = [ Param.make "value" TInt64 ""; Param.make "modulus" TInt64 "" ] + // parameters = [ Param.make "dividend" TInt64 ""; Param.make "divisor" TInt64 "" ] // returnType = TypeReference.result TInt64 TString // description = - // "Returns the result of wrapping around so that {{0 <= res < modulus}}, as a . - // If is positive, returns {{Ok res}}. Returns an {{Error}} if is {{0}} or negative. + // "Returns the result of wrapping around so that {{0 <= res < divisor}}, as a . + // If is positive, returns {{Ok res}}. Returns an {{Error}} if is {{0}} or negative. // Use if you want the remainder after division, which has a different behavior for negative numbers." // fn = // (function @@ -101,14 +101,15 @@ let fns : List = { name = fn "int64Remainder" 0 typeParams = [] - parameters = [ Param.make "value" TInt64 ""; Param.make "divisor" TInt64 "" ] + parameters = + [ Param.make "dividend" TInt64 ""; Param.make "divisor" TInt64 "" ] returnType = TypeReference.result TInt64 TString description = - "Returns the integer remainder left over after dividing by + "Returns the integer remainder left over after dividing by , as a . For example, {{Int64.remainder 15 6 == Ok 3}}. The remainder will be - negative only if {{ < 0}}. + negative only if {{ < 0}}. The sign of doesn't influence the outcome. diff --git a/packages/darklang/languageTools/_runtimeErrors/cli.dark b/packages/darklang/languageTools/_runtimeErrors/cli.dark deleted file mode 100644 index 14722d0134..0000000000 --- a/packages/darklang/languageTools/_runtimeErrors/cli.dark +++ /dev/null @@ -1,8 +0,0 @@ -module Darklang = - module LanguageTools = - module RuntimeErrors = - module Cli = - type Error = - | NoExpressionsToExecute - | UncaughtException of String * List - | NonIntReturned of actuallyReturned: LanguageTools.RuntimeTypes.Dval.Dval \ No newline at end of file diff --git a/packages/darklang/languageTools/_runtimeErrors/execution.dark b/packages/darklang/languageTools/_runtimeErrors/execution.dark deleted file mode 100644 index 2dd53a6749..0000000000 --- a/packages/darklang/languageTools/_runtimeErrors/execution.dark +++ /dev/null @@ -1,272 +0,0 @@ -module Darklang = - module LanguageTools = - module RuntimeErrors = - module Execution = - type Error = - | MatchExprUnmatched of RuntimeTypes.Dval.Dval - | MatchExprPatternWrongType of String * RuntimeTypes.Dval.Dval - | MatchExprEnumPatternWrongCount of - String * - expected: Int64 * - actual: Int64 - - | NonStringInStringInterpolation of RuntimeTypes.Dval.Dval - - | ConstDoesntExist of RuntimeTypes.FQConstantName.FQConstantName - - | FieldAccessNotRecord of RuntimeTypes.ValueType * String - - | FieldAccessFieldDoesntExist of - typeName: RuntimeTypes.FQTypeName.FQTypeName * - invalidFieldName: String - - | EnumConstructionCaseNotFound of - typeName: RuntimeTypes.FQTypeName.FQTypeName * - caseName: String - - | WrongNumberOfFnArgs of - fn: RuntimeTypes.FQFnName.FQFnName * - expectedTypeArgs: Int64 * - expectedArgs: Int64 * - actualTypeArgs: Int64 * - actualArgs: Int64 - - | RecordConstructionFieldDoesntExist of - typeName: RuntimeTypes.FQTypeName.FQTypeName * - invalidFieldName: String - | RecordConstructionMissingField of - RuntimeTypes.FQTypeName.FQTypeName * - missingFieldName: String - | RecordConstructionDuplicateField of - RuntimeTypes.FQTypeName.FQTypeName * - duplicateFieldName: String - - - let toSegments (e: Error) : ErrorOutput = - match e with - | MatchExprUnmatched dv -> - let summary = - [ ErrorSegment.ErrorSegment.String "No match for " - ErrorSegment.ErrorSegment.InlineValue dv ] - - // TODO include patterns in error message - let extraExplanation = [] - - ErrorOutput - { summary = summary - extraExplanation = extraExplanation - actual = [] - expected = [] } - - - | MatchExprEnumPatternWrongCount(caseName, expected, actual) -> - let summary = - [ ErrorSegment.ErrorSegment.String caseName - ErrorSegment.ErrorSegment.String " pattern is expecting " - ErrorSegment.ErrorSegment.Count( - expected, - ErrorSegment.ErrorSegment.String "field", - ErrorSegment.ErrorSegment.String "fields" - ) - ErrorSegment.ErrorSegment.String ", but " - ErrorSegment.ErrorSegment.String caseName - ErrorSegment.ErrorSegment.String " has " - ErrorSegment.ErrorSegment.Count( - actual, - ErrorSegment.ErrorSegment.String "field", - ErrorSegment.ErrorSegment.String "fields" - ) ] - - let extraExplanation = [] - - ErrorOutput - { summary = summary - extraExplanation = extraExplanation - actual = [] - expected = [] } - - - | MatchExprPatternWrongType(patternType, dv) -> - let summary = - // "Cannot match Int64 value 6 with a Float pattern" - [ ErrorSegment.ErrorSegment.String "Cannot match " - ErrorSegment.ErrorSegment.TypeOfValue dv - ErrorSegment.ErrorSegment.String " value " - ErrorSegment.ErrorSegment.InlineValue dv - ErrorSegment.ErrorSegment.String " with " - ErrorSegment.ErrorSegment.IndefiniteArticle - ErrorSegment.ErrorSegment.String patternType - ErrorSegment.ErrorSegment.String " pattern" ] - - let extraExplanation = [] - - ErrorOutput - { summary = summary - extraExplanation = extraExplanation - actual = [] - expected = [] } - - // Expected String in string interpolation, got 1.0 - | NonStringInStringInterpolation dv -> - let summary = - [ ErrorSegment.ErrorSegment.String - "Expected String in string interpolation, got " - ErrorSegment.ErrorSegment.InlineValue dv ] - - let extraExplanation = [] - - ErrorOutput - { summary = summary - extraExplanation = extraExplanation - actual = [] - expected = [] } - - | ConstDoesntExist name -> - let summary = - [ ErrorSegment.ErrorSegment.String "Constant " - ErrorSegment.ErrorSegment.ConstantName name - ErrorSegment.ErrorSegment.String " doesn't exist" ] - - let extraExplanation = [] - - ErrorOutput - { summary = summary - extraExplanation = extraExplanation - actual = [] - expected = [] } - - | FieldAccessFieldDoesntExist(typeName, invalidFieldName) -> - let summary = - [ ErrorSegment.ErrorSegment.String "No field named " - ErrorSegment.ErrorSegment.FieldName invalidFieldName - ErrorSegment.ErrorSegment.String " in " - ErrorSegment.ErrorSegment.TypeName typeName - ErrorSegment.ErrorSegment.String " record" ] - - let extraExplanation = [] - - ErrorOutput - { summary = summary - extraExplanation = extraExplanation - actual = [] - expected = [] } - - | EnumConstructionCaseNotFound(typeName, caseName) -> - let summary = - [ ErrorSegment.ErrorSegment.String "There is no case named " - ErrorSegment.ErrorSegment.FieldName caseName - ErrorSegment.ErrorSegment.String " in " - ErrorSegment.ErrorSegment.TypeName typeName ] - - let extraExplanation = [] - - ErrorOutput - { summary = summary - extraExplanation = extraExplanation - actual = [] - expected = [] } - - - | FieldAccessNotRecord(vt, fieldName) -> - let summary = - [ ErrorSegment.ErrorSegment.String "Attempting to access field " - ErrorSegment.ErrorSegment.FieldName fieldName - ErrorSegment.ErrorSegment.String " of a " - ErrorSegment.ErrorSegment.ValueType vt - ErrorSegment.ErrorSegment.String - " (field access only works with records)" ] - - let extraExplanation = [] - - ErrorOutput - { summary = summary - extraExplanation = extraExplanation - actual = [] - expected = [] } - - - | WrongNumberOfFnArgs(fn, - expectedTypeArgs, - expectedArgs, - actualTypeArgs, - actualArgs) -> - let summary = - [ ErrorSegment.ErrorSegment.FunctionName fn - ErrorSegment.ErrorSegment.String " has " - ErrorSegment.ErrorSegment.Count( - expectedTypeArgs, - ErrorSegment.ErrorSegment.String "type parameter", - ErrorSegment.ErrorSegment.String "type parameters" - ) - ErrorSegment.ErrorSegment.String " and " - ErrorSegment.ErrorSegment.Count( - expectedArgs, - ErrorSegment.ErrorSegment.String "parameter", - ErrorSegment.ErrorSegment.String "parameters" - ) - ErrorSegment.ErrorSegment.String ", but here was called with " - ErrorSegment.ErrorSegment.Count( - actualTypeArgs, - ErrorSegment.ErrorSegment.String "type argument", - ErrorSegment.ErrorSegment.String "type arguments" - ) - ErrorSegment.ErrorSegment.String " and " - ErrorSegment.ErrorSegment.Count( - actualArgs, - ErrorSegment.ErrorSegment.String "argument", - ErrorSegment.ErrorSegment.String "arguments" - ) - ErrorSegment.ErrorSegment.String "." ] - - let extraExplanation = [] - - ErrorOutput - { summary = summary - extraExplanation = extraExplanation - actual = [] - expected = [] } - - | RecordConstructionFieldDoesntExist(typeName, invalidFieldName) -> - let summary = - [ ErrorSegment.ErrorSegment.String "Unexpected field " - ErrorSegment.ErrorSegment.FieldName invalidFieldName - ErrorSegment.ErrorSegment.String " in " - ErrorSegment.ErrorSegment.TypeName typeName ] - - let extraExplanation = [] - - ErrorOutput - { summary = summary - extraExplanation = extraExplanation - actual = [] - expected = [] } - - | RecordConstructionMissingField(typeName, missingFieldName) -> - let summary = - [ ErrorSegment.ErrorSegment.String "Missing field " - ErrorSegment.ErrorSegment.FieldName missingFieldName - ErrorSegment.ErrorSegment.String " in " - ErrorSegment.ErrorSegment.TypeName typeName ] - - let extraExplanation = [] - - ErrorOutput - { summary = summary - extraExplanation = extraExplanation - actual = [] - expected = [] } - - | RecordConstructionDuplicateField(typeName, duplicateFieldName) -> - let summary = - [ ErrorSegment.ErrorSegment.String "Duplicate field " - ErrorSegment.ErrorSegment.FieldName duplicateFieldName - ErrorSegment.ErrorSegment.String " in " - ErrorSegment.ErrorSegment.TypeName typeName ] - - let extraExplanation = [] - - ErrorOutput - { summary = summary - extraExplanation = extraExplanation - actual = [] - expected = [] } \ No newline at end of file diff --git a/packages/darklang/languageTools/_runtimeErrors/int.dark b/packages/darklang/languageTools/_runtimeErrors/int.dark deleted file mode 100644 index 45a857d0a2..0000000000 --- a/packages/darklang/languageTools/_runtimeErrors/int.dark +++ /dev/null @@ -1,47 +0,0 @@ -module Darklang = - module LanguageTools = - module RuntimeErrors = - module Int = - type Error = - | DivideByZeroError - | OutOfRange - | NegativeExponent - | NegativeModulus - | ZeroModulus - - let toSegments (e: Error) : ErrorOutput = - match e with - | DivideByZeroError -> - ErrorOutput - { summary = [ ErrorSegment.ErrorSegment.String "Division by zero" ] - extraExplanation = [] - actual = [] - expected = [] } - - | OutOfRange -> - ErrorOutput - { summary = [ ErrorSegment.ErrorSegment.String "Out of range" ] - extraExplanation = [] - actual = [] - expected = [] } - - | NegativeExponent -> - ErrorOutput - { summary = [ ErrorSegment.ErrorSegment.String "Negative exponent" ] - extraExplanation = [] - actual = [] - expected = [] } - - | NegativeModulus -> - ErrorOutput - { summary = [ ErrorSegment.ErrorSegment.String "Negative modulus" ] - extraExplanation = [] - actual = [] - expected = [] } - - | ZeroModulus -> - ErrorOutput - { summary = [ ErrorSegment.ErrorSegment.String "Zero modulus" ] - extraExplanation = [] - actual = [] - expected = [] } \ No newline at end of file diff --git a/packages/darklang/languageTools/_runtimeErrors/json.dark b/packages/darklang/languageTools/_runtimeErrors/json.dark deleted file mode 100644 index c4a944e7e7..0000000000 --- a/packages/darklang/languageTools/_runtimeErrors/json.dark +++ /dev/null @@ -1,39 +0,0 @@ -module Darklang = - module LanguageTools = - module RuntimeErrors = - module Json = - type Error = UnsupportedType of RuntimeTypes.TypeReference - - let toSegments (e: Error) : ErrorOutput = - match e with - | UnsupportedType typ -> - let summary = - [ ErrorSegment.ErrorSegment.String "Unsupported type in JSON: " - ErrorSegment.ErrorSegment.TypeReference typ ] - - let extraExplanation = - let parse = - RuntimeTypes.FQFnName.FQFnName.Builtin( - RuntimeTypes.FQFnName.Builtin { name = "jsonParse"; version = 0L } - ) - - let serialize = - RuntimeTypes.FQFnName.FQFnName.Builtin( - RuntimeTypes.FQFnName.Builtin - { name = "jsonSerialize"; version = 0L } - ) - - - [ ErrorSegment.ErrorSegment.String - ". Some types are not supported in Json serialization, and cannot be used as arguments to " - ErrorSegment.ErrorSegment.FunctionName parse - ErrorSegment.ErrorSegment.String " or " - ErrorSegment.ErrorSegment.FunctionName serialize ] - - ErrorOutput - { summary = summary - extraExplanation = extraExplanation - actual = [ ErrorSegment.ErrorSegment.TypeReference typ ] - expected = - [ ErrorSegment.ErrorSegment.String - "A supported type (Int64, String, etc)" ] } \ No newline at end of file diff --git a/packages/darklang/languageTools/_runtimeErrors/nameResolution.dark b/packages/darklang/languageTools/_runtimeErrors/nameResolution.dark deleted file mode 100644 index 08316d927b..0000000000 --- a/packages/darklang/languageTools/_runtimeErrors/nameResolution.dark +++ /dev/null @@ -1,86 +0,0 @@ -module Darklang = - module LanguageTools = - module RuntimeErrors = - module NameResolution = - type ErrorType = - | NotFound of names: List - | ExpectedEnumButNot of packageTypeID: uuid - | ExpectedRecordButNot of packageTypeID: uuid - | MissingEnumModuleName of caseName: String - | InvalidPackageName of names: List - - type NameType = - | Function - | Type - | Constant - - type Error = - { errorType: ErrorType - nameType: NameType } - - let toSegments (e: Error) : ErrorOutput = - match e.errorType with - | NotFound names -> - let nameType = - match e.nameType with - | Function -> "function" - | Type -> "type" - | Constant -> "constant" - - ErrorOutput - { summary = - [ ErrorSegment.ErrorSegment.String $"There is no {nameType} named " - ErrorSegment.ErrorSegment.InlineVarName( - Stdlib.String.join names "." - ) ] - extraExplanation = [] - actual = [] - expected = [] } - - | MissingEnumModuleName caseName -> - let summary = - [ ErrorSegment.ErrorSegment.String "Missing type name for enum case" - ErrorSegment.ErrorSegment.String ": " - ErrorSegment.ErrorSegment.String caseName ] - - ErrorOutput - { summary = summary - extraExplanation = [] - actual = [] - expected = [] } - - | InvalidPackageName names -> - let summary = - [ ErrorSegment.ErrorSegment.String "Invalid package name " - ErrorSegment.ErrorSegment.InlineVarName(Stdlib.String.join names ".") ] - - ErrorOutput - { summary = summary - extraExplanation = [] - actual = [] - expected = [] } - - | ExpectedRecordButNot packageTypeID -> - let summary = - [ ErrorSegment.ErrorSegment.String "Expected a record but " - ErrorSegment.ErrorSegment.TypeName( - RuntimeTypes.FQTypeName.FQTypeName.Package packageTypeID - ) - ErrorSegment.ErrorSegment.String " is an enum" ] - - ErrorOutput - { summary = summary - extraExplanation = [] - actual = [] - expected = [] } - - | _ -> - // RTETODO: - // | ExpectedEnumButNot - ErrorOutput - { summary = - [ ErrorSegment.ErrorSegment.String - "RTETODO NameResolution.toSegments" ] - extraExplanation = [] - actual = [] - expected = [] } \ No newline at end of file diff --git a/packages/darklang/languageTools/_runtimeErrors/runtimeErrors.dark b/packages/darklang/languageTools/_runtimeErrors/runtimeErrors.dark deleted file mode 100644 index 57cb719426..0000000000 --- a/packages/darklang/languageTools/_runtimeErrors/runtimeErrors.dark +++ /dev/null @@ -1,182 +0,0 @@ -module Darklang = - module LanguageTools = - module RuntimeErrors = - module ErrorSegment = - /// Error pretty printing - /// --------------- - /// Segments allow us to build error messages where the UI and CLI can both - /// decorate/link to the sources in a native way - type ErrorSegment = - // -- Basic types - | String of String - | Int of Int64 - | Ordinal of Int64 // 1st, 2nd, etc - | IndefiniteArticle // "a" or "an" (chosen based on the next segment) - | Count of Int64 * singular: ErrorSegment * plural: ErrorSegment // 2 errors, 1 error, etc - - // -- Functions - | FunctionName of LanguageTools.RuntimeTypes.FQFnName.FQFnName - /// Description from StdLib description fields. - /// Has markers like , that should be parsed and displayed (TODO: why parse?) - | Description of String - | ParamName of String - | InlineParamName of String - - // -- Types - | TypeName of LanguageTools.RuntimeTypes.FQTypeName.FQTypeName - | ShortTypeName of LanguageTools.RuntimeTypes.FQTypeName.FQTypeName - | TypeReference of LanguageTools.RuntimeTypes.TypeReference - | TypeOfValue of LanguageTools.RuntimeTypes.Dval.Dval // CLEANUP should these all just be ValueTypes? - | ValueType of LanguageTools.RuntimeTypes.ValueType - | FieldName of String // records and enums - | InlineFieldName of String // records and enums - - // -- Constants - | ConstantName of LanguageTools.RuntimeTypes.FQConstantName.FQConstantName - - // -- Variables - | DBName of String - | VarName of String - | InlineVarName of String - - // -- Dvals - | InlineValue of LanguageTools.RuntimeTypes.Dval.Dval // possibly shortened to be shown inline - | FullValue of LanguageTools.RuntimeTypes.Dval.Dval - - let toString (list: List) : String = - let reversed = Stdlib.List.reverse list - - let parts = - Stdlib.List.fold reversed [] (fun prevSegments segment -> - let newSegment = - match segment with - | String s -> s - | Int i -> Stdlib.Int64.toString i - | Ordinal i -> Stdlib.String.toOrdinal i - | Count(i, singular, plural) -> - let count = Stdlib.Int64.toString i - - if i == 1L then - let segment = toString [ singular ] - $"{count} {segment}" - else - let segment = toString [ plural ] - $"{count} {segment}" - - | IndefiniteArticle -> - match Stdlib.List.head prevSegments with - | None -> "" - | Some prev -> Stdlib.String.articleFor prev ++ " " - - | FunctionName fn -> PrettyPrinter.RuntimeTypes.fnName fn - - | Description d -> d - | ParamName p -> $"`{p}`" - | InlineParamName p -> p // Inline versions don't have quotes - | TypeName t -> PrettyPrinter.RuntimeTypes.typeName t - | ConstantName c -> PrettyPrinter.RuntimeTypes.constantName c - | ShortTypeName t -> - // TODO: make it short - PrettyPrinter.RuntimeTypes.typeName t - | TypeReference t -> PrettyPrinter.RuntimeTypes.typeReference t - | TypeOfValue dv -> PrettyPrinter.RuntimeTypes.Dval.valueTypeName dv - | ValueType vt -> PrettyPrinter.RuntimeTypes.valueType vt - | FieldName f -> $"`{f}`" - | InlineFieldName f -> f - | DBName db -> db - | VarName v -> $"`{v}`" - | InlineVarName v -> v - | InlineValue dv -> - (PrettyPrinter.RuntimeTypes.dval dv) - |> Stdlib.String.ellipsis 10L - |> Stdlib.String.splitOnNewline - |> Stdlib.String.join "" - | FullValue dv -> PrettyPrinter.RuntimeTypes.dval dv - | segment -> - $"(RTETODO toString parts {(Builtin.jsonSerialize segment)})" - - Stdlib.List.push prevSegments newSegment) - - Stdlib.String.join parts "" - - - type ErrorOutput = - { summary: List - // Summary can be used on its own or concatenated with extraExplanation - extraExplanation: List - actual: List - expected: List } - - // TODO: update SqlCompilerRuntimeError to refer to - // SqlCompiler.RuntimeErrors.Error, with other Error types here - // maybe as cases of that (e.g. TypeCheckerError, NameResolutionError) - - type Error = - | CliError of LanguageTools.RuntimeErrors.Cli.Error - | TypeCheckerError of LanguageTools.RuntimeErrors.TypeChecker.Error - | NameResolutionError of LanguageTools.RuntimeErrors.NameResolution.Error - | SqlCompilerRuntimeError of Error - | ExecutionError of LanguageTools.RuntimeErrors.Execution.Error - | JsonError of LanguageTools.RuntimeErrors.Json.Error - | IntError of LanguageTools.RuntimeErrors.Int.Error - - | OldStringErrorTODO of String - - let sqlErrorTemplate = - "You're using our new experimental Datastore query compiler. It compiles your lambdas into optimized (and partially indexed) Datastore queries, which should be reasonably fast.\n\nUnfortunately, we hit a snag while compiling your lambda. We only support a subset of Darklang's functionality, but will be expanding it in the future.\n\nSome Darklang code is not supported in DB::query lambdas for now, and some of it won't be supported because it's an odd thing to do in a datastore query. If you think your operation should be supported, let us know in #general in Discord.\n\n Error: " - - module Error = - // RTETODO: move most of the logic here to the individual modules, - // and then follow up here - // - let toSegments (e: Error) : ErrorOutput = - match e with - | OldStringErrorTODO s -> - ErrorOutput - { summary = [ ErrorSegment.ErrorSegment.String s ] - extraExplanation = [] - actual = [] - expected = [] } - - // | CliError err -> Cli.Error.toSegments err - - | TypeCheckerError err -> TypeChecker.Error.toSegments err - - | NameResolutionError err -> NameResolution.toSegments err - - | ExecutionError err -> Execution.toSegments err - - | SqlCompilerRuntimeError err -> - let innerOutput = toSegments err - - { innerOutput with - summary = - Stdlib.List.append - [ (ErrorSegment.ErrorSegment.String sqlErrorTemplate) ] - innerOutput.summary } - - | JsonError err -> Json.toSegments err - - | IntError err -> Int.toSegments err - - let toString (e: Error) : String = - let s = toSegments e - - let explanation = - ErrorSegment.toString (Stdlib.List.append s.summary s.extraExplanation) - - let actual = ErrorSegment.toString s.actual - let actual = if actual == "" then "" else $"\nActual: {actual}" - let expected = ErrorSegment.toString s.expected - let expected = if expected == "" then "" else $"\nExpected: {expected}" - - if actual == "" && expected == "" then - explanation - else - $"{explanation}\n{expected}{actual}" - - - type ErrorMessage = ErrorString of String - - let toErrorMessage (e: Error) : ErrorMessage = - ErrorMessage.ErrorString(toString e) \ No newline at end of file diff --git a/packages/darklang/languageTools/_runtimeErrors/typeChecker.dark b/packages/darklang/languageTools/_runtimeErrors/typeChecker.dark deleted file mode 100644 index 15b8d056b7..0000000000 --- a/packages/darklang/languageTools/_runtimeErrors/typeChecker.dark +++ /dev/null @@ -1,365 +0,0 @@ -module Darklang = - module LanguageTools = - module RuntimeErrors = - // - type Option = Stdlib.Option.Option - type ID = LanguageTools.ID - type TLID = LanguageTools.TLID - - // language - type RTTypeReference = LanguageTools.RuntimeTypes.TypeReference - - type RTDval = LanguageTools.RuntimeTypes.Dval.Dval - - type RTTypeName = LanguageTools.RuntimeTypes.FQTypeName.FQTypeName - - type RTFnName = LanguageTools.RuntimeTypes.FQFnName.FQFnName - type RTParam = LanguageTools.RuntimeTypes.Param - - // RuntimeErrors - type REErrorOutput = LanguageTools.RuntimeErrors.ErrorOutput - // - - - // CLEANUP consider renaming this module to RuntimeTypeChecker - module TypeChecker = - type Context = - | FunctionCallParameter of - fnName: RTFnName * - parameter: RTParam * - paramIndex: Int64 - - | FunctionCallResult of fnName: RTFnName * returnType: RTTypeReference - - | RecordField of - recordTypeName: RTTypeName * - fieldName: String * - fieldType: RTTypeReference - - | DictKey of key: String * typ: RTTypeReference - - | EnumField of - enumTypeName: RTTypeName * - caseName: String * - fieldIndex: Int64 * - fieldCount: Int64 * - fieldType: RTTypeReference - - | DBQueryVariable of varName: String * expected: RTTypeReference - - | DBSchemaType of name: String * expectedType: RTTypeReference - - | ListIndex of index: Int64 * listTyp: RTTypeReference * parent: Context - - | TupleIndex of - index: Int64 * - elementType: RTTypeReference * - parent: Context - - | FnValResult of returnType: RTTypeReference - - - type ErrorType = - | ValueNotExpectedType of - actualValue: RTDval * - expectedType: RTTypeReference - | TypeDoesntExist of RTTypeName - - type Error = - { errorType: ErrorType - context: Context } - - - module Error = - let valuePath (context: Context) : String = - // match context with - // | TCK.FunctionCallParameter(_, parameter, _) -> parameter.name - // | TCK.FunctionCallResult(_, _) -> "result" - // | TCK.RecordField(_, fieldName, _) -> fieldName - // | TCK.DictKey(key, _) -> $".{key}" - // | TCK.EnumField(_, caseName, _, _, _) -> caseName - // | TCK.DBSchemaType(dbName, _) -> dbName - // | TCK.DBQueryVariable(varName, _) -> varName - // | TCK.ListIndex(index, _, parent) -> valuePath parent + $"[{index}]" - // | TCK.TupleIndex(index, _, parent) -> valuePath parent + $"[{index}]" - "RTETODO: valuePath" - - let rootContext (context: Context) : Context = - match context with - | FunctionCallParameter _ -> context - | FunctionCallResult _ -> context - | RecordField _ -> context - | DictKey _ -> context - | EnumField _ -> context - | DBSchemaType _ -> context - | DBQueryVariable _ -> context - | ListIndex(_, _, parent) -> rootContext parent - | TupleIndex(_, _, parent) -> rootContext parent - | FnValResult _ -> context - - - /// Return the segments describing the context as a short name, used in the description of errors - let contextSummary (context: Context) : List = - match context with - | FunctionCallParameter(fnName, parameter, paramIndex) -> - [ ErrorSegment.ErrorSegment.FunctionName fnName - ErrorSegment.ErrorSegment.String "'s " - ErrorSegment.ErrorSegment.Ordinal(paramIndex + 1L) - ErrorSegment.ErrorSegment.String " argument (" - ErrorSegment.ErrorSegment.ParamName parameter.name - ErrorSegment.ErrorSegment.String ")" ] - - | FunctionCallResult(fnName, returnType) -> - [ ErrorSegment.ErrorSegment.FunctionName fnName - ErrorSegment.ErrorSegment.String "'s return value" ] - - - | RecordField(recordType, fieldName, _) -> - [ ErrorSegment.ErrorSegment.TypeName recordType - ErrorSegment.ErrorSegment.String "'s " - ErrorSegment.ErrorSegment.FieldName fieldName - ErrorSegment.ErrorSegment.String " field" ] - - // | DictKey(key, _) -> - // let typeName = - // FQName.BuiltIn { name = TypeName.TypeName "Dict"; modules = []; version = 0 } - // [ TypeName typeName; String "'s "; FieldName key; String " value" ] - - | EnumField(enumType, caseName, fieldIndex, _, _) -> - [ ErrorSegment.ErrorSegment.TypeName enumType - ErrorSegment.ErrorSegment.String "." - ErrorSegment.ErrorSegment.InlineFieldName caseName - ErrorSegment.ErrorSegment.String "'s " - ErrorSegment.ErrorSegment.Ordinal(fieldIndex + 1L) - ErrorSegment.ErrorSegment.String " argument" ] - - | DBSchemaType(dbName, expectedType) -> - [ ErrorSegment.ErrorSegment.String "DB " - ErrorSegment.ErrorSegment.DBName dbName - ErrorSegment.ErrorSegment.String "'s value" ] - - | DBQueryVariable(varName, _) -> - [ ErrorSegment.ErrorSegment.String "Variable " - ErrorSegment.ErrorSegment.VarName varName ] - - | TupleIndex(index, typ, parent) -> - let rootContext = rootContext parent - - [ String "In " ] - @ contextSummary rootContext - @ [ String ", the nested value "; VarName(valuePath context) ] - - | ListIndex(index, typ, parent) -> - let rootContext = rootContext parent - - [ String "In " ] - @ contextSummary rootContext - @ [ String ", the nested value "; VarName(valuePath context) ] - - | FnValResult(_) -> - [ ErrorSegment.ErrorSegment.String "Function return value" ] - - - let rec contextAsActualExpected - (argument: RuntimeTypes.Dval.Dval) - (context: Context) - : List * List = - - // RTETODO: We do actual and expected in the same function so that we can display - // them the same way. This hasn't been ported for all Context types, but - // should be. - let defaultActual = - [ ErrorSegment.ErrorSegment.IndefiniteArticle - ErrorSegment.ErrorSegment.TypeOfValue argument - ErrorSegment.ErrorSegment.String ": " - ErrorSegment.ErrorSegment.FullValue argument ] - - match context with - | FunctionCallParameter(fnName, parameter, paramIndex) -> - let segments = - [ ErrorSegment.ErrorSegment.String "(" - ErrorSegment.ErrorSegment.InlineParamName parameter.name - ErrorSegment.ErrorSegment.String ": " - ErrorSegment.ErrorSegment.TypeReference parameter.typ - ErrorSegment.ErrorSegment.String ")" ] - - (defaultActual, segments) - - - | FunctionCallResult(fnName, returnType) -> - // format: - // Option - let segment = [ ErrorSegment.ErrorSegment.TypeReference returnType ] - (defaultActual, segment) - - - | RecordField(recordType, fieldName, fieldType) -> - // format: - // ({ name : string; ... }) // some description - - // TODO: fetch the type and lookup the field definition as a comment - let comment = [] - // let comment = - // if fieldDef.description = "" then - // [] - // else - // [ String " // "; Description fieldDef.description ] - - let segment = - Stdlib.List.append - [ ErrorSegment.ErrorSegment.String "({ " - ErrorSegment.ErrorSegment.InlineFieldName fieldName - ErrorSegment.ErrorSegment.String ": " - ErrorSegment.ErrorSegment.TypeReference fieldType - ErrorSegment.ErrorSegment.String "; ... })" ] - comment - - (defaultActual, segment) - - - | DictKey(key, typ) -> - // format: - // ({ "name" : String; ... }) - let segment = - [ ErrorSegment.ErrorSegment.String "({ " - ErrorSegment.ErrorSegment.InlineFieldName key - ErrorSegment.ErrorSegment.String ": " - ErrorSegment.ErrorSegment.TypeReference typ - ErrorSegment.ErrorSegment.String "; ... })" ] - - defaultActual, segment - - - | EnumField(enumType, caseName, fieldIndex, fieldCount, fieldType) -> - // format: - // Ok (..., string, ...) // some description - // RTETODO: extract description from the type definition later - - let prefix = - if fieldIndex == 0L then - [] - else - [ ErrorSegment.ErrorSegment.String "..., " ] - - let suffix = - if fieldIndex == fieldCount - 1L then - [] - else - [ ErrorSegment.ErrorSegment.String ", ..." ] - - let openParen = - if fieldCount > 0L then - [ ErrorSegment.ErrorSegment.String "(" ] - else - [] - - let closeParen = - if fieldCount > 0L then - [ ErrorSegment.ErrorSegment.String ")" ] - else - [] - - //CLEANUP de-duplicate these 2 segments a bit - let fieldTypeSegment = - [ ErrorSegment.ErrorSegment.ShortTypeName enumType - ErrorSegment.ErrorSegment.String "." - ErrorSegment.ErrorSegment.InlineFieldName caseName - ErrorSegment.ErrorSegment.String " " ] - |> Stdlib.List.append openParen - |> Stdlib.List.append prefix - |> Stdlib.List.append - [ ErrorSegment.ErrorSegment.TypeReference fieldType ] - |> Stdlib.List.append suffix - |> Stdlib.List.append closeParen - - let argumentSegment = - [ ErrorSegment.ErrorSegment.ShortTypeName enumType - ErrorSegment.ErrorSegment.String "." - ErrorSegment.ErrorSegment.InlineFieldName caseName - ErrorSegment.ErrorSegment.String " " ] - |> Stdlib.List.append openParen - |> Stdlib.List.append prefix - |> Stdlib.List.append - [ ErrorSegment.ErrorSegment.TypeOfValue argument ] - |> Stdlib.List.append suffix - |> Stdlib.List.append closeParen - - (argumentSegment, fieldTypeSegment) - - - | DBSchemaType(dbName, expectedType) -> - let segment = [ ErrorSegment.ErrorSegment.TypeReference expectedType ] - (defaultActual, segment) - - - | DBQueryVariable(varName, expected) -> - let segment = - [ ErrorSegment.ErrorSegment.String "(" - ErrorSegment.ErrorSegment.InlineVarName varName - ErrorSegment.ErrorSegment.String ": " - ErrorSegment.ErrorSegment.TypeReference expected - ErrorSegment.ErrorSegment.String ")" ] - - (defaultActual, segment) - - | ListIndex(index, typ, parent) -> - defaultActual, [ ErrorSegment.ErrorSegment.TypeReference typ ] - - | TupleIndex(index, typ, parent) -> - defaultActual, [ ErrorSegment.ErrorSegment.TypeReference typ ] - - | FnValResult(returnType) -> - defaultActual, [ ErrorSegment.ErrorSegment.TypeReference returnType ] - - - let contextVerb (context: Context) : String = - match context with - | FunctionCallParameter _ -> "passed" - | FunctionCallResult _ -> "returned" - | RecordField _ -> "passed" - | DictKey _ -> "passed" - | EnumField _ -> "passed" - | DBSchemaType _ -> "passed" - | DBQueryVariable _ -> "passed" - | ListIndex _ -> "passed" - | TupleIndex _ -> "passed" - | FnValResult _ -> "returned" - - - - let toSegments (e: Error) : REErrorOutput = - match e.errorType with - | ValueNotExpectedType(argument, expected) -> - let summary = - Stdlib.List.append - (contextSummary e.context) - [ ErrorSegment.ErrorSegment.String " should be " - ErrorSegment.ErrorSegment.IndefiniteArticle - ErrorSegment.ErrorSegment.TypeReference expected ] - - let extraExplanation = - [ ErrorSegment.ErrorSegment.String ". However, " - ErrorSegment.ErrorSegment.IndefiniteArticle - ErrorSegment.ErrorSegment.TypeOfValue argument - ErrorSegment.ErrorSegment.String " (" - ErrorSegment.ErrorSegment.InlineValue argument - ErrorSegment.ErrorSegment.String ") was " - ErrorSegment.ErrorSegment.String(contextVerb e.context) - ErrorSegment.ErrorSegment.String " instead." ] - - let (actual, expected) = contextAsActualExpected argument e.context - - ErrorOutput - { summary = summary - extraExplanation = extraExplanation - actual = actual - expected = expected } - - | _ -> - ErrorOutput - { summary = - [ ErrorSegment.ErrorSegment.String - "RTETODO typeChecker.toSegments" ] - extraExplanation = [] - actual = [] - expected = [] } \ No newline at end of file diff --git a/packages/darklang/languageTools/runtimeErrors.dark b/packages/darklang/languageTools/runtimeErrors.dark new file mode 100644 index 0000000000..2f5d11c8cb --- /dev/null +++ b/packages/darklang/languageTools/runtimeErrors.dark @@ -0,0 +1,165 @@ +module Darklang = + module LanguageTools = + module RuntimeTypes = + module RuntimeError = + module TypeCheckers = + type PathPart = + | TuplePart of index: Int32 + | ListItem of index: Int32 + | DictEntry of key: String + | RecordField of typeName: FQTypeName.FQTypeName * fieldName: String + | EnumField of + typeName: FQTypeName.FQTypeName * + caseName: String * + fieldIndex: Int32 * + fieldName: Option * + fieldCount: Int32 + | FunctionCallParameter of + fnName: FQFnName.FQFnName * + paramName: String * + paramIndex: Int32 + | FunctionCallResult of fnName: FQFnName.FQFnName + + type Path = List + + type Error = + | ValueNotExpectedType of + path: Path * + expected: TypeReference * + actual: Dval + + module Bools = + type Error = + | AndOnlySupportsBooleans of gotLeft: ValueType * gotRight: ValueType + | OrOnlySupportsBooleans of gotLeft: ValueType * gotRight: ValueType + | ConditionRequiresBool of actualValueType: ValueType * actualValue: Dval + + module Ints = + type Error = + | DivideByZeroError + | OutOfRange + | NegativeExponent + | NegativeModulus // negative divisors are not allowed + | ZeroModulus // a divisor of zero is not allowed + + module Strings = + type Error = + | NonStringInInterpolation of vt: ValueType * dv: Dval + | InvalidStringAppend + + module Lists = + type Error = + | TriedToAddMismatchedData of + expectedType: ValueType * + actualType: ValueType * + actualValue: Dval + + module Dicts = + type Error = + | TriedToAddKeyAfterAlreadyPresent of key: String + | TriedToAddMismatchedData of + expectedType: ValueType * + actualType: ValueType * + actualValue: Dval + + module Lets = + type Error = PatternDoesNotMatch of dval: Dval * pat: LetPattern + + module Matches = + type Error = | MatchUnmatched + + module Enums = + type Error = + | ConstructionWrongNumberOfFields of + typeName: FQTypeName.FQTypeName * + caseName: String * + expectedFieldCount: Int64 * + actualFieldCount: Int64 + | ConstructionCaseNotFound of + typeName: FQTypeName.FQTypeName * + caseName: String + | ConstructionFieldOfWrongType of + caseName: String * + fieldName: String * + fieldIndex: Int64 * + expectedType: TypeReference * + actualType: ValueType * + actualValue: Dval + + module Records = + type Error = + | CreationEmptyKey + | CreationMissingField of fieldName: String + | CreationDuplicateField of fieldName: String + | CreationFieldTypeNotRecord of name: FQTypeName.FQTypeName + | CreationFieldNotExpected of fieldName: String + | CreationFieldOfWrongType of + fieldName: String * + expectedType: TypeReference * + actualType: ValueType + | FieldAccessFieldNotFound of fieldName: String + | FieldAccessNotRecord of actualType: ValueType + | UpdateNotRecord of actualType: ValueType + | UpdateFieldOfWrongType of + fieldName: String * + expectedType: TypeReference * + actualType: ValueType + | UpdateFieldNotExpected of fieldName: String + + module Unwraps = + type Error = + | GotNone + | GotError of err: Dval + | NonOptionOrResult of actual: Dval + | MultipleArgs of args: List + + module Jsons = + type Error = + | UnsupportedType of TypeReference + | CannotSerializeTypeValueCombo of Dval * TypeReference + + module CLIs = + type Error = + | NoExpressionsToExecute + | NonIntReturned of actuallyReturned: Dval + + type Error = + | Bool of Bools.Error + | Int of Ints.Error + | String of Strings.Error + | List of Lists.Error + | Dict of Dicts.Error + | Let of Lets.Error + | VariableNotFound of attemptedVarName: String + | EqualityCheckOnIncompatibleTypes of left: ValueType * right: ValueType + | IfConditionNotBool of actualValue: Dval * actualValueType: ValueType + | Match of Matches.Error + | ParseTimeNameResolution of NameResolutionError + | TypeNotFound of name: FQTypeName.FQTypeName + | FnNotFound of name: FQFnName.FQFnName + | ConstNotFound of name: FQConstantName.FQConstantName + | WrongNumberOfTypeArgsForType of + fn: FQTypeName.FQTypeName * + expected: Int64 * + actual: Int64 + | Record of Records.Error + | Enum of Enums.Error + | Unwrap of Unwraps.Error + | WrongNumberOfTypeArgsForFn of + fn: FQFnName.FQFnName * + expected: Int64 * + actual: Int64 + | TooManyArgsForFn of + fn: FQFnName.FQFnName * + expected: Int64 * + actual: Int64 + | TooManyArgsForLambda of + lambdaExprId: id * + expected: Int64 * + actual: Int64 + | ExpectedApplicableButNot of actualTyp: ValueType * actualValue: Dval + | Json of Jsons.Error + | CLI of CLIs.Error + | CannotMergeValues of left: ValueType * right: ValueType + | TypeChecker of err: TypeCheckers.Error + | UncaughtException of reference: Uuid \ No newline at end of file diff --git a/packages/darklang/languageTools/runtimeTypes.dark b/packages/darklang/languageTools/runtimeTypes.dark index 71a95c5f6c..783f1326f7 100644 --- a/packages/darklang/languageTools/runtimeTypes.dark +++ b/packages/darklang/languageTools/runtimeTypes.dark @@ -190,14 +190,6 @@ module Darklang = // References //| DDB of String - - module RuntimeError = - // TODO: migrate this to another file - type Error = Unit - - - - type BuiltInParam = { name: String typ: TypeReference diff --git a/packages/darklang/prettyPrinter/runtimeError.dark b/packages/darklang/prettyPrinter/runtimeError.dark new file mode 100644 index 0000000000..ca3f187cd2 --- /dev/null +++ b/packages/darklang/prettyPrinter/runtimeError.dark @@ -0,0 +1,666 @@ +module Darklang = + module PrettyPrinter = + module RuntimeTypes = + // + type FQFnName = LanguageTools.RuntimeTypes.FQFnName.FQFnName + type FQConstantName = LanguageTools.RuntimeTypes.FQConstantName.FQConstantName + type Dval = LanguageTools.RuntimeTypes.Dval + type TypeReference = LanguageTools.RuntimeTypes.TypeReference + type ValueType = LanguageTools.RuntimeTypes.ValueType + // + + module RuntimeError = + /// Error pretty printing + /// --------------- + /// Segments allow us to build error messages where the UI and CLI can both + /// decorate/link to the sources in a native way + type ErrorSegment = + // -- Basic types + | String of String + | Int of Int64 + | Ordinal of Int64 // 1st, 2nd, etc + | IndefiniteArticle // "a" or "an" (chosen based on the next segment) + | Count of Int64 * singular: ErrorSegment * plural: ErrorSegment // 2 errors, 1 error, etc + + // -- Functions + | FunctionName of FQFnName + /// Description from StdLib description fields. + /// Note: may include markers like ``, to be parsed and displayed differently. + | Description of String + | ParamName of String + | InlineParamName of String + + // -- Types + | TypeName of FQFnName + | ShortTypeName of FQFnName + | TypeReference of TypeReference + | TypeOfValue of Dval // CLEANUP should these all just be ValueTypes? + | ValueType of ValueType + | FieldName of String // records and enums + | InlineFieldName of String // records and enums + + // -- Constants + | ConstantName of FQConstantName + + // -- Variables + //| DBName of String + | VarName of String + | InlineVarName of String + + // -- Dvals + | InlineValue of Dval // possibly shortened to be shown inline + | FullValue of Dval + + type ES = ErrorSegment + + type ErrorSegments = List + + + let segmentsToString (segments: ErrorSegments) : String = + let reversed = Stdlib.List.reverse segments + + let parts = + Stdlib.List.fold reversed [] (fun prevSegments segment -> + let newSegment = + match segment with + | String s -> s + | Int i -> Stdlib.Int64.toString i + | Ordinal i -> Stdlib.String.toOrdinal i + | Count(i, singular, plural) -> + let count = Stdlib.Int64.toString i + + if i == 1L then + let segment = toString [ singular ] + $"{count} {segment}" + else + let segment = toString [ plural ] + $"{count} {segment}" + + | IndefiniteArticle -> + match Stdlib.List.head prevSegments with + | None -> "" + | Some prev -> Stdlib.String.articleFor prev ++ " " + + | FunctionName fn -> fnName fn + + | Description d -> d + | ParamName p -> $"`{p}`" + | InlineParamName p -> p // Inline versions don't have quotes + | TypeName t -> typeName t + | ConstantName c -> constantName c + | ShortTypeName t -> + // TODO: make it short + typeName t + | TypeReference t -> typeReference t + | TypeOfValue dv -> Dval.valueTypeName dv + | ValueType vt -> valueType vt + | FieldName f -> $"`{f}`" + | InlineFieldName f -> f + | DBName db -> db + | VarName v -> $"`{v}`" + | InlineVarName v -> v + | InlineValue dv -> + (dval dv) + |> Stdlib.String.ellipsis 10L + |> Stdlib.String.splitOnNewline + |> Stdlib.String.join "" + | FullValue dv -> dval dv + | segment -> + $"(RTETODO toString parts {(Builtin.jsonSerialize segment)})" + + Stdlib.List.push prevSegments newSegment) + + Stdlib.String.join parts "" + + + let toSegments + (e: LanguageTools.RuntimeTypes.RuntimeError.Error) + : ErrorSegments = + match e with + // TODO: fill in a lot + (* + // | CliError err -> Cli.Error.toSegments err + + | TypeCheckerError err -> TypeChecker.Error.toSegments err + + | NameResolutionError err -> NameResolution.toSegments err + + | ExecutionError err -> Execution.toSegments err + + | SqlCompilerRuntimeError err -> + let sqlErrorTemplate = + "You're using our new experimental Datastore query compiler. It compiles your lambdas into optimized (and partially indexed) Datastore queries, which should be reasonably fast.\n\nUnfortunately, we hit a snag while compiling your lambda. We only support a subset of Darklang's functionality, but will be expanding it in the future.\n\nSome Darklang code is not supported in DB::query lambdas for now, and some of it won't be supported because it's an odd thing to do in a datastore query. If you think your operation should be supported, let us know in #general in Discord.\n\n Error: " + + let innerOutput = toSegments err + + + { innerOutput with + summary = + Stdlib.List.append + [ (ES.String sqlErrorTemplate) ] + innerOutput.summary } + + | JsonError err -> Json.toSegments err + + | IntError err -> Int.toSegments err + *) + | _ -> + [ ES.String + $"TODO: {Builtin.jsonSerialize e}" ] + + + + let toString (e: LanguageTools.RuntimeTypes.RuntimeError.Error) : String = + e |> toSegments |> segmentsToString + + + type ErrorMessage = ErrorString of String + + let toErrorMessage + (e: LanguageTools.RuntimeTypes.RuntimeError.Error) + : ErrorMessage = + ErrorMessage.ErrorString(toString e) + + +(* + module Execution = + let toSegments (e: Error) : ErrorOutput = + match e with + | MatchExprUnmatched dv -> + // TODO include patterns in error message + [ ES.String "No match for "; ES.InlineValue dv ] + + | MatchExprEnumPatternWrongCount(caseName, expected, actual) -> + [ ES.String caseName + ES.String " pattern is expecting " + ES.Count( + expected, + ES.String "field", + ES.String "fields" + ) + ES.String ", but " + ES.String caseName + ES.String " has " + ES.Count( + actual, + ES.String "field", + ES.String "fields" + ) ] + + + | MatchExprPatternWrongType(patternType, dv) -> + // "Cannot match Int64 value 6 with a Float pattern" + [ ES.String "Cannot match " + ES.TypeOfValue dv + ES.String " value " + ES.InlineValue dv + ES.String " with " + ES.IndefiniteArticle + ES.String patternType + ES.String " pattern" ] + + + // Expected String in string interpolation, got 1.0 + | NonStringInStringInterpolation dv -> + [ ES.String "Expected String in string interpolation, got " + ES.InlineValue dv ] + + + | ConstDoesntExist name -> + [ ES.String "Constant " + ES.ConstantName name + ES.String " doesn't exist" ] + + | FieldAccessFieldDoesntExist(typeName, invalidFieldName) -> + [ ES.String "No field named " + ES.FieldName invalidFieldName + ES.String " in " + ES.TypeName typeName + ES.String " record" ] + + + | EnumConstructionCaseNotFound(typeName, caseName) -> + [ ES.String "There is no case named " + ES.FieldName caseName + ES.String " in " + ES.TypeName typeName ] + + + | FieldAccessNotRecord(vt, fieldName) -> + [ ES.String "Attempting to access field " + ES.FieldName fieldName + ES.String " of a " + ES.ValueType vt + ES.String " (field access only works with records)" ] + + + | WrongNumberOfFnArgs(fn, + expectedTypeArgs, + expectedArgs, + actualTypeArgs, + actualArgs) -> + [ ES.FunctionName fn + ES.String " has " + ES.Count( + expectedTypeArgs, + ES.String "type parameter", + ES.String "type parameters" + ) + ES.String " and " + ES.Count( + expectedArgs, + ES.String "parameter", + ES.String "parameters" + ) + ES.String ", but here was called with " + ES.Count( + actualTypeArgs, + ES.String "type argument", + ES.String "type arguments" + ) + ES.String " and " + ES.Count( + actualArgs, + ES.String "argument", + ES.String "arguments" + ) + ES.String "." ] + + | RecordConstructionFieldDoesntExist(typeName, invalidFieldName) -> + [ ES.String "Unexpected field " + ES.FieldName invalidFieldName + ES.String " in " + ES.TypeName typeName ] + + + | RecordConstructionMissingField(typeName, missingFieldName) -> + [ ES.String "Missing field " + ES.FieldName missingFieldName + ES.String " in " + ES.TypeName typeName ] + + | RecordConstructionDuplicateField(typeName, duplicateFieldName) -> + [ ES.String "Duplicate field " + ES.FieldName duplicateFieldName + ES.String " in " + ES.TypeName typeName ] + *) + + + +(* + + module Darklang = + module LanguageTools = + module RuntimeErrors = + module Int = + let toSegments (e: Error) : ErrorOutput = + match e with + | DivideByZeroError -> + [ ES.String "Division by zero" ] + | OutOfRange -> [ ES.String "Out of range" ] + + | NegativeExponent -> + [ ES.String "Negative exponent" ] + + | NegativeModulus -> + [ ES.String "Negative modulus" ] + + | ZeroModulus -> [ ES.String "Zero modulus" ] + *) + + + +(* +module Json = + let toSegments (e: Error) : ErrorOutput = + match e with + | UnsupportedType typ -> + let summary = + [ ES.String "Unsupported type in JSON: " + ES.TypeReference typ ] + + let extraExplanation = + let parse = + RuntimeTypes.FQFnName.FQFnName.Builtin( + RuntimeTypes.FQFnName.Builtin { name = "jsonParse"; version = 0L } + ) + + let serialize = + RuntimeTypes.FQFnName.FQFnName.Builtin( + RuntimeTypes.FQFnName.Builtin + { name = "jsonSerialize"; version = 0L } + ) + + + [ ES.String + ". Some types are not supported in Json serialization, and cannot be used as arguments to " + ES.FunctionName parse + ES.String " or " + ES.FunctionName serialize ] + + ErrorOutput + { summary = summary + extraExplanation = extraExplanation + actual = [ ES.TypeReference typ ] + expected = + [ ES.String + "A supported type (Int64, String, etc)" ] } + *) + + + +(* + module NameResolution = + let toSegments (e: Error) : ErrorOutput = + match e.errorType with + | NotFound names -> + let nameType = + match e.nameType with + | Function -> "function" + | Type -> "type" + | Constant -> "constant" + + ErrorOutput + { summary = + [ ES.String $"There is no {nameType} named " + ES.InlineVarName( + Stdlib.String.join names "." + ) ] + extraExplanation = [] + actual = [] + expected = [] } + + | MissingEnumModuleName caseName -> + [ ES.String "Missing type name for enum case" + ES.String ": " + ES.String caseName ] + + | InvalidPackageName names -> + [ ES.String "Invalid package name " + ES.InlineVarName(Stdlib.String.join names ".") ] + + | ExpectedRecordButNot packageTypeID -> + [ ES.String "Expected a record but " + ES.TypeName( + RuntimeTypes.FQTypeName.FQTypeName.Package packageTypeID + ) + ES.String " is an enum" ] + + | _ -> + [ ES.String "RTETODO NameResolution.toSegments" ] + *) + + + +(* + module TypeChecker = + module Error = + let valuePath (context: Context) : String = + // match context with + // | TCK.FunctionCallParameter(_, parameter, _) -> parameter.name + // | TCK.FunctionCallResult(_, _) -> "result" + // | TCK.RecordField(_, fieldName, _) -> fieldName + // | TCK.DictKey(key, _) -> $".{key}" + // | TCK.EnumField(_, caseName, _, _, _) -> caseName + // | TCK.DBSchemaType(dbName, _) -> dbName + // | TCK.DBQueryVariable(varName, _) -> varName + // | TCK.ListIndex(index, _, parent) -> valuePath parent + $"[{index}]" + // | TCK.TupleIndex(index, _, parent) -> valuePath parent + $"[{index}]" + "RTETODO: valuePath" + + + /// Return the segments describing the context as a short name, used in the description of errors + let contextSummary (context: Context) : List = + match context with + | FunctionCallParameter(fnName, parameter, paramIndex) -> + [ ES.FunctionName fnName + ES.String "'s " + ES.Ordinal(paramIndex + 1L) + ES.String " argument (" + ES.ParamName parameter.name + ES.String ")" ] + + | FunctionCallResult(fnName, returnType) -> + [ ES.FunctionName fnName + ES.String "'s return value" ] + + + | RecordField(recordType, fieldName, _) -> + [ ES.TypeName recordType + ES.String "'s " + ES.FieldName fieldName + ES.String " field" ] + + // | DictKey(key, _) -> + // let typeName = + // FQName.BuiltIn { name = TypeName.TypeName "Dict"; modules = []; version = 0 } + // [ TypeName typeName; String "'s "; FieldName key; String " value" ] + + | EnumField(enumType, caseName, fieldIndex, _, _) -> + [ ES.TypeName enumType + ES.String "." + ES.InlineFieldName caseName + ES.String "'s " + ES.Ordinal(fieldIndex + 1L) + ES.String " argument" ] + + | DBSchemaType(dbName, expectedType) -> + [ ES.String "DB " + ES.DBName dbName + ES.String "'s value" ] + + | DBQueryVariable(varName, _) -> + [ ES.String "Variable " + ES.VarName varName ] + + | TupleIndex(index, typ, parent) -> + let rootContext = rootContext parent + + [ String "In " ] + @ contextSummary rootContext + @ [ String ", the nested value "; VarName(valuePath context) ] + + | ListIndex(index, typ, parent) -> + let rootContext = rootContext parent + + [ String "In " ] + @ contextSummary rootContext + @ [ String ", the nested value "; VarName(valuePath context) ] + + + + let rec contextAsActualExpected + (argument: RuntimeTypes.Dval.Dval) + (context: Context) + : List * List = + + // RTETODO: We do actual and expected in the same function so that we can display + // them the same way. This hasn't been ported for all Context types, but + // should be. + let defaultActual = + [ ES.IndefiniteArticle + ES.TypeOfValue argument + ES.String ": " + ES.FullValue argument ] + + match context with + | FunctionCallParameter(fnName, parameter, paramIndex) -> + let segments = + [ ES.String "(" + ES.InlineParamName parameter.name + ES.String ": " + ES.TypeReference parameter.typ + ES.String ")" ] + + (defaultActual, segments) + + + | FunctionCallResult(fnName, returnType) -> + // format: + // Option + let segment = [ ES.TypeReference returnType ] + (defaultActual, segment) + + + | RecordField(recordType, fieldName, fieldType) -> + // format: + // ({ name : string; ... }) // some description + + // TODO: fetch the type and lookup the field definition as a comment + let comment = [] + // let comment = + // if fieldDef.description = "" then + // [] + // else + // [ String " /// "; Description fieldDef.description ] + + let segment = + Stdlib.List.append + [ ES.String "({ " + ES.InlineFieldName fieldName + ES.String ": " + ES.TypeReference fieldType + ES.String "; ... })" ] + comment + + (defaultActual, segment) + + + | DictKey(key, typ) -> + // format: + // ({ "name" : String; ... }) + let segment = + [ ES.String "({ " + ES.InlineFieldName key + ES.String ": " + ES.TypeReference typ + ES.String "; ... })" ] + + defaultActual, segment + + + | EnumField(enumType, caseName, fieldIndex, fieldCount, fieldType) -> + // format: + // Ok (..., string, ...) // some description + // RTETODO: extract description from the type definition later + + let prefix = + if fieldIndex == 0L then + [] + else + [ ES.String "..., " ] + + let suffix = + if fieldIndex == fieldCount - 1L then + [] + else + [ ES.String ", ..." ] + + let openParen = + if fieldCount > 0L then + [ ES.String "(" ] + else + [] + + let closeParen = + if fieldCount > 0L then + [ ES.String ")" ] + else + [] + + //CLEANUP de-duplicate these 2 segments a bit + let fieldTypeSegment = + [ ES.ShortTypeName enumType + ES.String "." + ES.InlineFieldName caseName + ES.String " " ] + |> Stdlib.List.append openParen + |> Stdlib.List.append prefix + |> Stdlib.List.append + [ ES.TypeReference fieldType ] + |> Stdlib.List.append suffix + |> Stdlib.List.append closeParen + + let argumentSegment = + [ ES.ShortTypeName enumType + ES.String "." + ES.InlineFieldName caseName + ES.String " " ] + |> Stdlib.List.append openParen + |> Stdlib.List.append prefix + |> Stdlib.List.append + [ ES.TypeOfValue argument ] + |> Stdlib.List.append suffix + |> Stdlib.List.append closeParen + + (argumentSegment, fieldTypeSegment) + + + // | DBSchemaType(dbName, expectedType) -> + // let segment = [ ES.TypeReference expectedType ] + // (defaultActual, segment) + + + // | DBQueryVariable(varName, expected) -> + // let segment = + // [ ES.String "(" + // ES.InlineVarName varName + // ES.String ": " + // ES.TypeReference expected + // ES.String ")" ] + + // (defaultActual, segment) + + | ListIndex(index, typ, parent) -> + defaultActual, [ ES.TypeReference typ ] + + | TupleIndex(index, typ, parent) -> + defaultActual, [ ES.TypeReference typ ] + + + let contextVerb (context: Context) : String = + match context with + | FunctionCallParameter _ -> "passed" + | FunctionCallResult _ -> "returned" + | RecordField _ -> "passed" + | DictKey _ -> "passed" + | EnumField _ -> "passed" + | DBSchemaType _ -> "passed" + | DBQueryVariable _ -> "passed" + | ListIndex _ -> "passed" + | TupleIndex _ -> "passed" + + + + let toSegments (e: Error) : REErrorOutput = + match e.errorType with + | ValueNotExpectedType(argument, expected) -> + let summary = + Stdlib.List.append + (contextSummary e.context) + [ ES.String " should be " + ES.IndefiniteArticle + ES.TypeReference expected ] + + let extraExplanation = + [ ES.String ". However, " + ES.IndefiniteArticle + ES.TypeOfValue argument + ES.String " (" + ES.InlineValue argument + ES.String ") was " + ES.String(contextVerb e.context) + ES.String " instead." ] + + let (actual, expected) = contextAsActualExpected argument e.context + + ErrorOutput + { summary = summary + extraExplanation = extraExplanation + actual = actual + expected = expected } + + | _ -> + [ ES.String "RTETODO typeChecker.toSegments" ] + *) \ No newline at end of file diff --git a/packages/darklang/prettyPrinter/_runtimeTypes.dark b/packages/darklang/prettyPrinter/runtimeTypes.dark similarity index 93% rename from packages/darklang/prettyPrinter/_runtimeTypes.dark rename to packages/darklang/prettyPrinter/runtimeTypes.dark index 30fa348822..7666ce9fef 100644 --- a/packages/darklang/prettyPrinter/_runtimeTypes.dark +++ b/packages/darklang/prettyPrinter/runtimeTypes.dark @@ -2,17 +2,19 @@ module Darklang = module PrettyPrinter = module RuntimeTypes = let nameResolutionError - (nr: LanguageTools.RuntimeErrors.NameResolution.Error) + (nre: LanguageTools.RuntimeTypes.NameResolutionError) : String = - match nr.errorType with + match nre with | NotFound names -> Stdlib.String.join names "." - | InvalidPackageName names -> Stdlib.String.join names "." - | ExpectedEnumButNot -> - "Unexpected: stringification of NRE in RT Pretty-Printer for ExpectedEnumButNot" - | ExpectedRecordButNot -> - "Unexpected: stringification of NRE in RT Pretty-Printer for ExpectedRecordButNot" - | MissingEnumModuleName -> - "Unexpected: stringification of NRE in RT Pretty-Printer for MissingEnumModuleName" + | InvalidName names -> Stdlib.String.join names "." + + let nameResolution<'a> + (nr: LanguageTools.RuntimeTypes.NameResolution<'a>) + (f: 'a -> String) + : String = + match nr with + | Ok a -> f a + | Error e -> nameResolutionError e let packageName @@ -96,34 +98,43 @@ module Darklang = | Builtin b -> FQFnName.builtIn b | Package p -> FQFnName.package p + + let typeReference (t: LanguageTools.RuntimeTypes.TypeReference) : String = match t with | TUnit -> "Unit" | TBool -> "Bool" - | TInt64 -> "Int64" - | TUInt64 -> "UInt64" + | TInt8 -> "Int8" | TUInt8 -> "UInt8" | TInt16 -> "Int16" | TUInt16 -> "UInt16" | TInt32 -> "Int32" | TUInt32 -> "UInt32" + | TInt64 -> "Int64" + | TUInt64 -> "UInt64" | TInt128 -> "Int128" | TUInt128 -> "UInt128" + | TFloat -> "Float" + | TChar -> "Char" | TString -> "String" + | TUuid -> "Uuid" + | TDateTime -> "DateTime" - | TList inner -> $"List<{typeReference inner}>" - | TDict inner -> $"Dict<{typeReference inner}>" | TTuple(first, second, theRest) -> (Stdlib.List.append [ first; second ] theRest) |> Stdlib.List.map (fun item -> typeReference item) |> Stdlib.String.join " * " |> fun parts -> "(" ++ parts ++ ")" + | TList inner -> $"List<{typeReference inner}>" + + | TDict inner -> $"Dict<{typeReference inner}>" + | TCustomType(typ, args) -> let argsPart = match args with @@ -141,6 +152,7 @@ module Darklang = $"{typeNamePart}{argsPart}" + | TFn(args, ret) -> let argPart = args @@ -150,7 +162,9 @@ module Darklang = $"{argPart} -> {typeReference ret}" - | TDB inner -> $"DB<{typeReference inner}>" + + //| TDB inner -> $"DB<{typeReference inner}>" + | TVariable varName -> "'" ++ varName @@ -159,29 +173,33 @@ module Darklang = match t with | KTUnit -> "Unit" | KTBool -> "Bool" - | KTInt64 -> "Int64" - | KTUInt64 -> "UInt64" + | KTInt8 -> "Int8" | KTUInt8 -> "UInt8" | KTInt16 -> "Int16" | KTUInt16 -> "UInt16" | KTInt32 -> "Int32" | KTUInt32 -> "UInt32" + | KTInt64 -> "Int64" + | KTUInt64 -> "UInt64" | KTInt128 -> "Int128" | KTUInt128 -> "UInt128" + | KTFloat -> "Float" + | KTChar -> "Char" | KTString -> "String" + | KTDateTime -> "DateTime" | KTUuid -> "Uuid" - | KTList typ -> $"List<{valueType typ}>" - | KTDict typ -> $"Dict<{valueType typ}>" | KTTuple(t1, t2, trest) -> (Stdlib.List.append [ t1; t2 ] trest) |> Stdlib.List.map (fun item -> valueType item) |> Stdlib.String.join ", " |> fun s -> $"({s})" + | KTList typ -> $"List<{valueType typ}>" + | KTDict typ -> $"Dict<{valueType typ}>" | KTFn(argTypes, retType) -> (Stdlib.List.push argTypes retType) @@ -200,7 +218,7 @@ module Darklang = (typeName name) ++ typeArgsPortion - | KTDB typ -> $"Datastore<{valueType typ}>" + //| KTDB typ -> $"Datastore<{valueType typ}>" let valueType (vt: LanguageTools.RuntimeTypes.ValueType) : String = @@ -211,7 +229,8 @@ module Darklang = module Dval = let valueTypeName (dv: LanguageTools.RuntimeTypes.Dval) : String = - dv |> LanguageTools.RuntimeTypes.Dval.toValueType |> valueType + //dv |> LanguageTools.RuntimeTypes.Dval.toValueType |> valueType + "TODO - use a builtin instead of the thing that was here" let makeSpaces (len: Int64) : String = (Stdlib.List.repeat len " ") |> Builtin.unwrap |> Stdlib.String.join "" @@ -237,14 +256,14 @@ module Darklang = $"'{c}'" | DString s -> $"\"{s}\"" - | DInt64 i -> Builtin.int64ToString i - | DUInt64 i -> Builtin.uint64ToString i | DInt8 i -> Builtin.int8ToString i | DUInt8 i -> Builtin.uint8ToString i | DInt16 i -> Builtin.int16ToString i | DUInt16 i -> Builtin.uint16ToString i | DInt32 i -> Builtin.int32ToString i | DUInt32 i -> Builtin.uint32ToString i + | DInt64 i -> Builtin.int64ToString i + | DUInt64 i -> Builtin.uint64ToString i | DInt128 i -> Builtin.int128ToString i | DUInt128 i -> Builtin.uint128ToString i @@ -255,18 +274,6 @@ module Darklang = | DUuid uuid -> $"<{valueTypeName}: {Stdlib.Uuid.toString uuid}>" - - | DList(vt, l) -> - if Stdlib.List.isEmpty l then - $"{valueTypeName} []" - else - let elems = - Stdlib.String.join - (Stdlib.List.map l (fun item -> withIndent indent item)) - ", " - - $"[{inl}{elems}{nl}]" - | DTuple(first, second, theRest) -> let l = Stdlib.List.append [ first; second ] theRest @@ -281,27 +288,18 @@ module Darklang = $"({inl}{long}{nl})" - | DRecord(_, typeName, typeArgs, o) -> - let strs = - o - |> Stdlib.Dict.toList - |> Stdlib.List.map (fun pair -> - let (key, value) = pair - $"{key}: {withIndent indent value}") - let elems = Stdlib.String.join strs $",{inl}" - let typeStr = typeName typeName + | DList(vt, l) -> + if Stdlib.List.isEmpty l then + $"{valueTypeName} []" + else + let elems = + Stdlib.String.join + (Stdlib.List.map l (fun item -> withIndent indent item)) + ", " - let typeArgsPart = - match typeArgs with - | [] -> "" - | args -> - args - |> Stdlib.List.map (fun t -> valueTypeName t) - |> Stdlib.String.join ", " - |> fun betweenBrackets -> "<" + betweenBrackets + ">" + $"[{inl}{elems}{nl}]" - typeStr ++ typeArgsPart ++ " {" ++ inl ++ elems ++ nl ++ "}" | DDict(_, d) -> if d == Stdlib.Dict.empty then @@ -317,6 +315,7 @@ module Darklang = let elems = Stdlib.String.join strs $",{inl}" "{" ++ inl ++ elems ++ nl ++ "}" + | DEnum(_, typeName, typeArgs, caseName, fields) -> let typeArgsPart = match typeArgs with @@ -352,6 +351,30 @@ module Darklang = $"{typeStr}{typeArgsPart}.{caseName}{fieldStr}" + | DRecord(_, typeName, typeArgs, o) -> + let strs = + o + |> Stdlib.Dict.toList + |> Stdlib.List.map (fun pair -> + let (key, value) = pair + $"{key}: {withIndent indent value}") + + let elems = Stdlib.String.join strs $",{inl}" + let typeStr = typeName typeName + + let typeArgsPart = + match typeArgs with + | [] -> "" + | args -> + args + |> Stdlib.List.map (fun t -> valueTypeName t) + |> Stdlib.String.join ", " + |> fun betweenBrackets -> "<" + betweenBrackets + ">" + + typeStr ++ typeArgsPart ++ " {" ++ inl ++ elems ++ nl ++ "}" + + + // TODO: DApplicable | DFnVal(NamedFn fnName) -> PrettyPrinter.RuntimeTypes.fnName fnName | DFnVal(Lambda impl) -> @@ -366,7 +389,7 @@ module Darklang = $"\\ {ps} {{ ... }}" - | DDB name -> $"<{valueTypeName}: {name}>" + //| DDB name -> $"<{valueTypeName}: {name}>" diff --git a/scripts/build/reload-packages b/scripts/build/reload-packages index 4a1feb88d9..d0739ced46 100755 --- a/scripts/build/reload-packages +++ b/scripts/build/reload-packages @@ -3,8 +3,8 @@ set -euo pipefail -grey="\033[1;30m" -reset="\033[0m" +# grey="\033[1;30m" +# reset="\033[0m" TEST=false PUBLISHED_FLAG= @@ -33,17 +33,17 @@ fi echo -e "Done loading packages to internal SQL tables" -if [[ "$TEST" != "true" ]]; then - echo "Waiting for BwdServer to be ready, so we can reload dark-packages canvas" - for i in {1..100}; do - if curl -s -o /dev/null "localhost:${DARK_CONFIG_BWDSERVER_KUBERNETES_PORT}" ; then - break - fi - printf '.' - sleep 0.1 - done - - echo -e "Reloading dark-packages canvas ${grey}($LOG_CANVAS)${reset}" - ./scripts/run-local-exec $PUBLISHED_FLAG reload-dark-packages >> $LOG_CANVAS 2>&1 - echo -e "Done reloading dark-packages canvas" -fi \ No newline at end of file +# if [[ "$TEST" != "true" ]]; then +# echo "Waiting for BwdServer to be ready, so we can reload dark-packages canvas" +# for i in {1..100}; do +# if curl -s -o /dev/null "localhost:${DARK_CONFIG_BWDSERVER_KUBERNETES_PORT}" ; then +# break +# fi +# printf '.' +# sleep 0.1 +# done + +# echo -e "Reloading dark-packages canvas ${grey}($LOG_CANVAS)${reset}" +# ./scripts/run-local-exec $PUBLISHED_FLAG reload-dark-packages >> $LOG_CANVAS 2>&1 +# echo -e "Done reloading dark-packages canvas" +# fi \ No newline at end of file