I already found a solution to my problem on stackoverflow but it is in Objective-C. See this link
DDMathParser - Getting tokens
I translated this into Swift as shown below. So what is the latest way to get tokens from a string and to get grouped tokens?
For example: 1 + ($a - (3 / 4))
Here is my try:
do{
let token = try Tokenizer(string: "1 + ($a - (3 /4))").tokenize()
for element in token {
print(element)
}
} catch {
print(error)
}
But I get the following messages:
MathParser.DecimalNumberToken
MathParser.OperatorToken
MathParser.OperatorToken
MathParser.VariableToken
MathParser.OperatorToken
MathParser.OperatorToken
MathParser.DecimalNumberToken
MathParser.OperatorToken
MathParser.DecimalNumberToken
MathParser.OperatorToken
MathParser.OperatorToken
How do I get the specific tokens from my string?
You're getting all of the tokens.
OperatorToken, DecimalNumberToken, etc all inherit from a RawToken superclass. RawToken defines a .string and a .range property.
The .string is the actual String, as extracted or inferred from the source. The .range is where in the original string the token is located.
It's important to note, however, that there may be tokens produced by the tokenizer that are not present in the original string. For example, tokens get injected by the Tokenizer when resolving implicit multiplication (3x turns in to 3 * x).
Added later:
If you want the final tree of how it all gets parsed, then you want the Expression:
let expression = try Expression(string: "1 + ($a - (3 / 4))")
At this point, you switch on expression.kind. It'll either be a number, a variable, or a function. function expressions have child expressions, representing the arguments to the function.
Related
Please check the attached code below.
Why Pattern raise Error? , "No exact matches in call to subscript"
What is the difference between A and B?
This is the text import from the console.
3
Everest 8849
K2 8611
Kangchenjunga 8586
This is code
struct Mountain {
let name: String
let height: Int
}
func highestmountain() {
var mtList = [Mountain]()
let N = Int(readLine()!)!
(0..<N)
.forEach { _ in
/* Pattern A */
readLine()!
.split(separator: " ")
.forEach {
mtList.append(Mountain(name: "\($0[0])", height: Int("\($0[1])")!)) // Error: No exact matches in call to subscript
}
/* Pattern B */
let reads = readLine()!
.split(separator: " ")
mtList.append(Mountain(name: "\(reads[0])", height: Int("\(reads[1])")!)) // works!
}
}
In Pattern A, you're using split(" ") which creates an array of Strings (or, more specifically, String.SubSequence), and then you call forEach on that array.
forEach calls the attached closure for each item in the array. So, with your second input line, for example, on the first forEach, $0 will be "Everest" and on the second call, it'll be 8849. However, in your code, you're attempting to get $0[0], but $0 is not an array -- it's a single String.SubSequence. Thus the error about the subscript.
Your second approach (Pattern B) works because reads is an Array of String.SubSequence, so using a subscript (ie the [0]) works.
Unrelated to your question, but it's worth noting that using subscripts like [1] will fail and crash the app if you haven't first checked to make sure that the array has enough items in it. Force unwrapping with a ! (like you do with Int(...)!) can also cause crashes.
I want to format a string, in Swift, with two potential arguments (using Format specifiers). The string to format may have a place for only the first argument, only the second argument, or both arguments. If I use the first or both arguments it works, but if I use only the second argument, it does not work. For instance:
let title = "M."
let name = "David"
let greetingFormat = "Hello %1$# %2$#"
print(String(format: greetingFormat, title, name))
// OUTPUT> Hello M. David
// OK
If I use only the first argument in the String to format:
let greetingFormat = "Hello %1$#"
print(String(format: greetingFormat, title, name))
// OUTPUT> Hello M.
// OK
But when using only the second argument
let greetingFormat = "Hello %2$#"
print(String(format: greetingFormat, title, name))
// OUTPUT> Hello M.
// NOT THE EXPECTED RESULT!
In the last case I expected "Hello David". Is it a bug? How can I obtain the intended result for the last case where only the second argument is used?
Remarks:
Please note that this problem occurs in the context of localization (i.e. the string to format comes from a Localizable.strings file), so I don’t have the possibility to remove unused argument directly.
The question does not relate to person’s name formatting. This is just taken as a example.
I answer my own question but all credit to #Martin R that provides the relevant information in comments.
It is not a bug, String(format:) does not support omitting positional parameters.
It is a known behavior since ObjectiveC, see: stackoverflow.com/a/2946880/1187415
If you only have String arguments you can use multiple String substitutions with String.replacingOccurrences(of:, with:) instead of String(format:).
More precision on the last solution. The following will work in the case that only one argument is used and also if both arguments are used in the greetingFormat String:
greetingFormat.replacingOccurrences(
of: "%1$#", with: title)
.replacingOccurrences(
of: "%2$#", with: name)
Of course, with String.replacingOccurrences(of:, with:) you can choose other identifiers for the substitution than %1$# and %2$#.
I find this amusing more than anything. I've fixed it, but I'm wondering about the cause. Here is the error: DataManager.swift:51:90: Expression was too complex to be solved in reasonable time; consider breaking up the expression into distinct sub-expressions. Why is it complaining? It seems like one of the most simple expressions possible.
The compiler points to the columns + ");"; section
func tableName() -> String { return("users"); }
func createTableStatement(schema: [String]) -> String {
var schema = schema;
schema.append("id string");
schema.append("created integer");
schema.append("updated integer");
schema.append("model blob");
var columns: String = ",".join(schema);
var statement = "create table if not exists " + self.tableName() + "(" + columns + ");";
return(statement);
}
the fix is:
var statement = "create table if not exists " + self.tableName();
statement += "(" + columns + ");";
this also works (via #efischency) but I don't like it as much because I think the ( get lost:
var statement = "create table if not exists \(self.tableName()) (\(columns))"
I am not an expert on compilers - I don't know if this answer will "change how you think in a meaningful way," but my understanding of the problem is this:
It has to do with type inference. Each time you use the + operator, Swift has to search through all of the possible overloads for + and infer which version of + you are using. I counted just under 30 overloads for the + operator. That's a lot of possibilities, and when you chain 4 or 5 + operations together and ask the compiler to infer all of the arguments, you are asking a lot more than it might appear at first glance.
That inference can get complicated - for example, if you add a UInt8 and an Int using +, the output will be an Int, but there's some work that goes into evaluating the rules for mixing types with operators.
And when you are using literals, like the String literals in your example, the compiler doing the work of converting the String literal to a String, and then doing the work of infering the argument and return types for the + operator, etc.
If an expression is sufficiently complex - i.e., it requires the compiler to make too many inferences about the arguments and the operators - it quits and tells you that it quit.
Having the compiler quit once an expression reaches a certain level of complexity is intentional. The alternative is to let the compiler try and do it, and see if it can, but that is risky - the compiler could go on trying forever, bog down, or just crash. So my understanding is that there is a static threshold for the complexity of an expression that the compiler will not go beyond.
My understanding is that the Swift team is working on compiler optimizations that will make these errors less common. You can learn a little bit about it on the Apple Developer forums by clicking on this link.
On the Dev forums, Chris Lattner has asked people to file these errors as radar reports, because they are actively working on fixing them.
That is how I understand it after reading a number of posts here and on the Dev forum about it, but my understanding of compilers is naive, and I am hoping that someone with a deeper knowledge of how they handle these tasks will expand on what I have written here.
This is almost same as the accepted answer but with some added dialogue (I had with Rob Napier, his other answers and Matt, Oliver, David from Slack) and links.
See the comments in this discussion. The gist of it is:
+ is heavily overloaded (Apple seems to have fixed this for some cases)
The + operator is heavily overloaded, as of now it has 27 different functions so if you are concatenating 4 strings ie you have 3 + operators the compiler has to check between 27 operators each time, so that's 27^3 times. But that's not it.
There is also a check to see if the lhs and rhs of the + functions are both valid if they are it calls through to core the append called. There you can see there are a number of somewhat intensive checks that can occur. If the string is stored non-contiguously, which appears to be the case if the string you’re dealing with is actually bridged to NSString. Swift then has to re-assemble all the byte array buffers into a single contiguous buffer and which requires creating new buffers along the way. and then you eventually get one buffer that contains the string you’re attempting to concatenate together.
In a nutshell there is 3 clusters of compiler checks that will slow you down ie each sub-expression has to be reconsidered in light of everything it might return. As a result concatenating strings with interpolation ie using " My fullName is \(firstName) \(LastName)" is much better than "My firstName is" + firstName + LastName since interpolation doesn't have any overloading
Swift 3 has made some improvements. For more information read How to merge multiple Arrays without slowing the compiler down?. Nonetheless the + operator is still overloaded and it's better to use string interpolation for longer strings
Usage of optionals (ongoing problem - solution available)
In this very simple project:
import UIKit
class ViewController: UIViewController {
let p = Person()
let p2 = Person2()
func concatenatedOptionals() -> String {
return (p2.firstName ?? "") + "" + (p2.lastName ?? "") + (p2.status ?? "")
}
func interpolationOptionals() -> String {
return "\(p2.firstName ?? "") \(p2.lastName ?? "")\(p2.status ?? "")"
}
func concatenatedNonOptionals() -> String {
return (p.firstName) + "" + (p.lastName) + (p.status)
}
func interpolatedNonOptionals() -> String {
return "\(p.firstName) \(p.lastName)\(p.status)"
}
}
struct Person {
var firstName = "Swift"
var lastName = "Honey"
var status = "Married"
}
struct Person2 {
var firstName: String? = "Swift"
var lastName: String? = "Honey"
var status: String? = "Married"
}
The compile time for the functions are as such:
21664.28ms /Users/Honey/Documents/Learning/Foundational/CompileTime/CompileTime/ViewController.swift:16:10 instance method concatenatedOptionals()
2.31ms /Users/Honey/Documents/Learning/Foundational/CompileTime/CompileTime/ViewController.swift:20:10 instance method interpolationOptionals()
0.96ms /Users/Honey/Documents/Learning/Foundational/CompileTime/CompileTime/ViewController.swift:24:10 instance method concatenatedNonOptionals()
0.82ms /Users/Honey/Documents/Learning/Foundational/CompileTime/CompileTime/ViewController.swift:28:10 instance method interpolatedNonOptionals()
Notice how crazy high the compilation duration for concatenatedOptionals is.
This can be solved by doing:
let emptyString: String = ""
func concatenatedOptionals() -> String {
return (p2.firstName ?? emptyString) + emptyString + (p2.lastName ?? emptyString) + (p2.status ?? emptyString)
}
which compiles in 88ms
The root cause of the problem is that the compiler doesn't identify the "" as a String. It's actually ExpressibleByStringLiteral
The compiler will see ?? and will have to loop through all types that have conformed to this protocol, till it finds a type that can be a default to String.
By Using emptyString which is hardcoded to String, the compiler no longer needs to loop through all conforming types of ExpressibleByStringLiteral
To learn how to log compilation times see here or here
Other similar answers by Rob Napier on SO:
Why string addition takes so long to build?
How to merge multiple Arrays without slowing the compiler down?
Swift Array contains function makes build times long
This is quite ridiculous no matter what you say! :)
But this gets passed easily
return "\(year) \(month) \(dayString) \(hour) \(min) \(weekDay)"
I had similar issue:
expression was too complex to be solved in reasonable time; consider breaking up the expression into distinct sub-expressions
In Xcode 9.3 line goes like this:
let media = entities.filter { (entity) -> Bool in
After changing it into something like this:
let media = entities.filter { (entity: Entity) -> Bool in
everything worked out.
Probably it has something to do with Swift compiler trying to infer data type from code around.
Great news - this seems to be fixed in the upcoming Xcode 13.
I was filing radar reports for this:
http://openradar.appspot.com/radar?id=4962454186491904
https://bugreport.apple.com/web/?problemID=39206436
... and Apple has just confirmed that this is fixed.
I have tested all cases that I have with complex expressions and SwiftUI code and everything seems to work great in Xcode 13.
Hi Alex,
Thanks for your patience, and thanks for your feedback. We believe this issue is resolved.
Please test with the latest Xcode 13 beta 2 release and update your feedback report with your results by logging into https://feedbackassistant.apple.com or by using the Feedback Assistant app.
In Swift 4, new method .split(separator:) is introduced by apple in String struct. So to split a string with whitespace which is faster for e.g..
let str = "My name is Sudhir"
str.components(separatedBy: " ")
//or
str.split(separator: " ")
Performance aside, there is an important difference between split(separator:) and components(separatedBy:) in how they treat empty subsequences.
They will produce different results if your input contains a trailing whitespace:
let str = "My name is Sudhir " // trailing space
str.split(separator: " ")
// ["My", "name", "is", "Sudhir"]
str.components(separatedBy: " ")
// ["My", "name", "is", "Sudhir", ""] ← Additional empty string
To have both produce the same result, use the omittingEmptySubsequences:false argument (which defaults to true):
// To get the same behavior:
str.split(separator: " ", omittingEmptySubsequences: false)
// ["My", "name", "is", "Sudhir", ""]
Details here:
https://developer.apple.com/documentation/swift/string/2894564-split
I have made sample test with following Code.
var str = """
One of those refinements is to the String API, which has been made a lot easier to use (while also gaining power) in Swift 4. In past versions of Swift, the String API was often brought up as an example of how Swift sometimes goes too far in favoring correctness over ease of use, with its cumbersome way of handling characters and substrings. This week, let’s take a look at how it is to work with strings in Swift 4, and how we can take advantage of the new, improved API in various situations. Sometimes we have longer, static strings in our apps or scripts that span multiple lines. Before Swift 4, we had to do something like inline \n across the string, add an appendOnNewLine() method through an extension on String or - in the case of scripting - make multiple print() calls to add newlines to a long output. For example, here is how TestDrive’s printHelp() function (which is used to print usage instructions for the script) looks like in Swift 3 One of those refinements is to the String API, which has been made a lot easier to use (while also gaining power) in Swift 4. In past versions of Swift, the String API was often brought up as an example of how Swift sometimes goes too far in favoring correctness over ease of use, with its cumbersome way of handling characters and substrings. This week, let’s take a look at how it is to work with strings in Swift 4, and how we can take advantage of the new, improved API in various situations. Sometimes we have longer, static strings in our apps or scripts that span multiple lines. Before Swift 4, we had to do something like inline \n across the string, add an appendOnNewLine() method through an extension on String or - in the case of scripting - make multiple print() calls to add newlines to a long output. For example, here is how TestDrive’s printHelp() function (which is used to print usage instructions for the script) looks like in Swift 3
"""
var newString = String()
for _ in 1..<9999 {
newString.append(str)
}
var methodStart = Date()
_ = newString.components(separatedBy: " ")
print("Execution time Separated By: \(Date().timeIntervalSince(methodStart))")
methodStart = Date()
_ = newString.split(separator: " ")
print("Execution time Split By: \(Date().timeIntervalSince(methodStart))")
I run above code on iPhone6 , Here are the results
Execution time Separated By: 8.27463299036026
Execution time Split By: 4.06880903244019
Conclusion : split(separator:) is faster than components(separatedBy:).
Maybe a little late to answer:
split is a native swift method
components is NSString Foundation method
When you play with them, they behave a little bit different:
str.components(separatedBy: "\n\n")
This call can give you some interesting results
str.split(separator: "\n\n")
This leads to an compile error as you must provide a single character.
In Swift 3, what is the recommended way to put (potentially lots of) additional information in an error/exception that the catcher can use to solve/handle the problem? In all the examples I've seen, they use enums with associated values, and that seems overly cumbersome/verbose for lots of information.
Specifically, I am writing a simple parser and want a place to store the affected line and column numbers (and potentially other information in the future), but without requiring that every handler explicitly declare those as associated values, as that would be a burden on the caller.
At this point I can basically see two ways of doing this, neither of which seems particularly elegant and both of which require defining two different things:
Define an outer enum error that represents the type of error, and for each case accept a parameter that is an object that contains the additional exception details, or
Use the object as the actual Error and pass in a case from an enum to its constructor to represent the actual error condition.
Both of these feel somewhat unclean to me though as they take two separate concepts to represent a simple idea, an error, and I'm just wondering if there's a nicer way to do this.
Are there any conventions or recommended ways to handle errors that need to contain potentially lots of additional information?
I don't know if there is a "recommended" way, perhaps someone else can
answer that or provide a better solution.
But one possible approach would be to use a struct (with properties) as the error type and use optional properties for values which need
not be provided. Example:
struct ParserError: Error {
enum Reason {
case invalidCharacter
case unexpectedEOF
}
let reason: Reason
let line: Int?
let column: Int?
init(reason: Reason, line: Int? = nil, column: Int? = nil) {
self.reason = reason
self.line = line
self.column = column
}
}
One might also want to adopt the LocalizedError protocol to
provide sensible error descriptions even if the
concrete error type is not known by the catcher (compare How to provide a localized description with an Error type in Swift?):
extension ParserError: LocalizedError {
public var errorDescription: String? {
var description: String
switch reason {
case .invalidCharacter:
description = "Invalid Character in input file"
case .unexpectedEOF:
description = "Unexpected end of file"
}
if let line = line {
description += ", line \(line)"
}
if let column = column {
description += ", column \(column)"
}
return description
}
}
Usage example:
func parse() throws {
// Throw error with line number, but without column:
throw ParserError(reason: .invalidCharacter, line: 13)
}
do {
try parse()
} catch let error {
print(error.localizedDescription)
}
Output:
Invalid Character in input file, line 13