Skip to content

Commit f1f1d20

Browse files
committed
Improve type descriptions
1 parent 6f58795 commit f1f1d20

File tree

3 files changed

+36
-59
lines changed

3 files changed

+36
-59
lines changed

ShapeScript/Interpreter.swift

Lines changed: 6 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -331,23 +331,6 @@ private func aOrAn(_ string: String, capitalized: Bool = false) -> String {
331331
return "\(capitalized ? prefix.capitalized : prefix) \(string)"
332332
}
333333

334-
private extension [String] {
335-
var typesDescription: String {
336-
var types = Set(self).sorted()
337-
if let index = types.firstIndex(of: "block") {
338-
types.append(types.remove(at: index))
339-
}
340-
switch types.count {
341-
case 1:
342-
return types[0]
343-
case 2:
344-
return "\(types[0]) or \(types[1])"
345-
default:
346-
return "\(types.dropLast().joined(separator: ", ")), or \(types.last!)"
347-
}
348-
}
349-
}
350-
351334
extension RuntimeErrorType {
352335
static func typeMismatch(
353336
for symbol: String,
@@ -357,16 +340,6 @@ extension RuntimeErrorType {
357340
.typeMismatch(for: symbol, index: -1, expected: expected, got: got)
358341
}
359342

360-
static func typeMismatch(
361-
for symbol: String,
362-
index: Int = -1,
363-
expected types: [String],
364-
got: String
365-
) -> RuntimeErrorType {
366-
let expected = types.typesDescription
367-
return .typeMismatch(for: symbol, index: index, expected: expected, got: got)
368-
}
369-
370343
static func typeMismatch(
371344
for name: String,
372345
index: Int = -1,
@@ -397,15 +370,6 @@ extension RuntimeErrorType {
397370
.missingArgument(for: symbol, index: 0, type: type)
398371
}
399372

400-
static func missingArgument(
401-
for symbol: String,
402-
index: Int = 0,
403-
types: [String]
404-
) -> RuntimeErrorType {
405-
let expected = types.typesDescription
406-
return .missingArgument(for: symbol, index: index, type: expected)
407-
}
408-
409373
static func missingArgument(
410374
for name: String,
411375
index: Int = 0,
@@ -424,14 +388,12 @@ extension RuntimeErrorType {
424388
}
425389

426390
static func unusedValue(type: ValueType) -> RuntimeErrorType {
427-
let typeDescription: String
428391
switch type {
429392
case let .list(type):
430-
typeDescription = type.errorDescription
393+
return unusedValue(type: type)
431394
default:
432-
typeDescription = type.errorDescription
395+
return unusedValue(type: type.errorDescription)
433396
}
434-
return unusedValue(type: typeDescription)
435397
}
436398

437399
static func unknownMember(_ name: String, of value: Value) -> RuntimeErrorType {
@@ -638,15 +600,11 @@ private func evaluateBlockParameters(
638600
do {
639601
try childContext.addValue(child)
640602
} catch {
641-
var types = type.childTypes.subtypes.map(\.errorDescription)
642-
if j == 0 {
643-
types.append("block")
644-
}
645603
throw RuntimeError(
646604
.typeMismatch(
647605
for: identifier.name,
648606
index: j > 0 ? j : -1,
649-
expected: types,
607+
expected: j == 0 ? type.childTypes.errorDescriptionOrBlock : type.childTypes.errorDescription,
650608
got: child.type.errorDescription
651609
),
652610
at: j < parameters.count ? parameters[j].range : range
@@ -1061,7 +1019,7 @@ extension Statement {
10611019
} else if !type.childTypes.isOptional {
10621020
throw RuntimeError(.missingArgument(
10631021
for: name,
1064-
types: type.childTypes.subtypes.map(\.errorDescription) + ["block"]
1022+
type: type.childTypes.errorDescriptionOrBlock
10651023
), at: range.upperBound ..< range.upperBound)
10661024
} else {
10671025
let childContext = context.push(type)
@@ -1218,7 +1176,7 @@ extension Expression {
12181176
// Blocks that require children can't be called without arguments
12191177
throw RuntimeError(.missingArgument(
12201178
for: name,
1221-
types: type.childTypes.subtypes.map(\.errorDescription) + ["block"]
1179+
type: type.childTypes.errorDescriptionOrBlock
12221180
), at: range.upperBound ..< range.upperBound)
12231181
}
12241182
return try RuntimeError.wrap(fn(context.push(type)), at: range)
@@ -1322,11 +1280,7 @@ extension Expression {
13221280
context.define(name, as: .option(value))
13231281
return .void
13241282
case let .prefix(op, expression):
1325-
let value = try expression.evaluate(
1326-
as: .union([.number, .list(.number)]),
1327-
for: String(op.rawValue),
1328-
in: context
1329-
)
1283+
let value = try expression.evaluate(as: .numberOrVector, for: op.rawValue, in: context)
13301284
switch op {
13311285
case .minus:
13321286
switch value {

ShapeScript/Types.swift

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,18 @@ extension ValueType {
230230
description(pluralized: false)
231231
}
232232

233+
var errorDescriptionOrBlock: String {
234+
let description = errorDescription
235+
if description.isEmpty || description == "any" {
236+
return "block"
237+
} else if description.contains(", or ") {
238+
return "\(description.replacingOccurrences(of: ", or ", with: ", ")), or block"
239+
} else if description.contains(" or ") {
240+
return "\(description.replacingOccurrences(of: " or ", with: ", ")), or block"
241+
}
242+
return "\(description) or block"
243+
}
244+
233245
fileprivate func description(pluralized: Bool) -> String {
234246
switch self {
235247
case .color: return "color\(pluralized ? "s" : "")"
@@ -268,15 +280,15 @@ extension ValueType {
268280
case let .list(type):
269281
return "list\(pluralized ? "s" : "") of \(type.description(pluralized: true))"
270282
case let .union(types):
271-
return types.sorted().description(pluralized: pluralized)
283+
return types.description(pluralized: pluralized)
272284
case .object: return "object\(pluralized ? "s" : "")"
273285
}
274286
}
275287
}
276288

277-
private extension [ValueType] {
289+
private extension Set<ValueType> {
278290
func description(pluralized: Bool) -> String {
279-
let types = Set(self).sorted().map { $0.description(pluralized: pluralized) }
291+
let types = simplifiedForDescription().map { $0.description(pluralized: pluralized) }.sorted()
280292
switch types.count {
281293
case 1:
282294
return types[0]
@@ -286,6 +298,17 @@ private extension [ValueType] {
286298
return "\(types.dropLast().joined(separator: ", ")), or \(types.last!)"
287299
}
288300
}
301+
302+
private func simplifiedForDescription() -> Self {
303+
var types = self
304+
if contains(.number) {
305+
types.remove(.radians)
306+
}
307+
if contains(.list(.number)) {
308+
types.remove(.list(.radians))
309+
}
310+
return types
311+
}
289312
}
290313

291314
// MARK: Function types

ShapeScriptTests/InterpreterTests.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2605,7 +2605,7 @@ final class InterpreterTests: XCTestCase {
26052605
XCTAssertEqual(error?.message, "Type mismatch")
26062606
XCTAssertEqual(error, RuntimeError(.typeMismatch(
26072607
for: "loop bounds",
2608-
expected: "range or list",
2608+
expected: "list or range",
26092609
got: "number"
26102610
), at: range))
26112611
}
@@ -2619,7 +2619,7 @@ final class InterpreterTests: XCTestCase {
26192619
XCTAssertEqual(error?.message, "Type mismatch")
26202620
XCTAssertEqual(error, RuntimeError(.typeMismatch(
26212621
for: "loop bounds",
2622-
expected: "range or list",
2622+
expected: "list or range",
26232623
got: "string"
26242624
), at: range))
26252625
}
@@ -2729,10 +2729,10 @@ final class InterpreterTests: XCTestCase {
27292729
XCTAssertThrowsError(try evaluate(parse(program), delegate: nil)) { error in
27302730
let error = try? XCTUnwrap(error as? RuntimeError)
27312731
XCTAssertEqual(error?.message, "Type mismatch")
2732-
XCTAssertEqual(error?.hint, "The loop bounds should be a range or list, not a color.")
2732+
XCTAssertEqual(error?.hint, "The loop bounds should be a list or range, not a color.")
27332733
XCTAssertEqual(error, RuntimeError(.typeMismatch(
27342734
for: "loop bounds",
2735-
expected: "range or list",
2735+
expected: "list or range",
27362736
got: "color"
27372737
), at: range))
27382738
}

0 commit comments

Comments
 (0)