Passed first set of builder tests.
This commit is contained in:
@@ -273,56 +273,56 @@ public class MTMathAtomFactory {
|
|||||||
"amalg" : MTMathAtom(type: .binaryOperator, value: "\u{2A3F}"),
|
"amalg" : MTMathAtom(type: .binaryOperator, value: "\u{2A3F}"),
|
||||||
|
|
||||||
// No limit operators
|
// No limit operators
|
||||||
"log" : MTMathAtomFactory.getOperator(withName: "log", limits: false),
|
"log" : MTMathAtomFactory.operatorWithName( "log", limits: false),
|
||||||
"lg" : MTMathAtomFactory.getOperator(withName: "lg", limits: false),
|
"lg" : MTMathAtomFactory.operatorWithName( "lg", limits: false),
|
||||||
"ln" : MTMathAtomFactory.getOperator(withName: "ln", limits: false),
|
"ln" : MTMathAtomFactory.operatorWithName( "ln", limits: false),
|
||||||
"sin" : MTMathAtomFactory.getOperator(withName: "sin", limits: false),
|
"sin" : MTMathAtomFactory.operatorWithName( "sin", limits: false),
|
||||||
"arcsin" : MTMathAtomFactory.getOperator(withName: "arcsin", limits: false),
|
"arcsin" : MTMathAtomFactory.operatorWithName( "arcsin", limits: false),
|
||||||
"sinh" : MTMathAtomFactory.getOperator(withName: "sinh", limits: false),
|
"sinh" : MTMathAtomFactory.operatorWithName( "sinh", limits: false),
|
||||||
"cos" : MTMathAtomFactory.getOperator(withName: "cos", limits: false),
|
"cos" : MTMathAtomFactory.operatorWithName( "cos", limits: false),
|
||||||
"arccos" : MTMathAtomFactory.getOperator(withName: "arccos", limits: false),
|
"arccos" : MTMathAtomFactory.operatorWithName( "arccos", limits: false),
|
||||||
"cosh" : MTMathAtomFactory.getOperator(withName: "cosh", limits: false),
|
"cosh" : MTMathAtomFactory.operatorWithName( "cosh", limits: false),
|
||||||
"tan" : MTMathAtomFactory.getOperator(withName: "tan", limits: false),
|
"tan" : MTMathAtomFactory.operatorWithName( "tan", limits: false),
|
||||||
"arctan" : MTMathAtomFactory.getOperator(withName: "arctan", limits: false),
|
"arctan" : MTMathAtomFactory.operatorWithName( "arctan", limits: false),
|
||||||
"tanh" : MTMathAtomFactory.getOperator(withName: "tanh", limits: false),
|
"tanh" : MTMathAtomFactory.operatorWithName( "tanh", limits: false),
|
||||||
"cot" : MTMathAtomFactory.getOperator(withName: "cot", limits: false),
|
"cot" : MTMathAtomFactory.operatorWithName( "cot", limits: false),
|
||||||
"coth" : MTMathAtomFactory.getOperator(withName: "coth", limits: false),
|
"coth" : MTMathAtomFactory.operatorWithName( "coth", limits: false),
|
||||||
"sec" : MTMathAtomFactory.getOperator(withName: "sec", limits: false),
|
"sec" : MTMathAtomFactory.operatorWithName( "sec", limits: false),
|
||||||
"csc" : MTMathAtomFactory.getOperator(withName: "csc", limits: false),
|
"csc" : MTMathAtomFactory.operatorWithName( "csc", limits: false),
|
||||||
"arg" : MTMathAtomFactory.getOperator(withName: "arg", limits: false),
|
"arg" : MTMathAtomFactory.operatorWithName( "arg", limits: false),
|
||||||
"ker" : MTMathAtomFactory.getOperator(withName: "ker", limits: false),
|
"ker" : MTMathAtomFactory.operatorWithName( "ker", limits: false),
|
||||||
"dim" : MTMathAtomFactory.getOperator(withName: "dim", limits: false),
|
"dim" : MTMathAtomFactory.operatorWithName( "dim", limits: false),
|
||||||
"hom" : MTMathAtomFactory.getOperator(withName: "hom", limits: false),
|
"hom" : MTMathAtomFactory.operatorWithName( "hom", limits: false),
|
||||||
"exp" : MTMathAtomFactory.getOperator(withName: "exp", limits: false),
|
"exp" : MTMathAtomFactory.operatorWithName( "exp", limits: false),
|
||||||
"deg" : MTMathAtomFactory.getOperator(withName: "deg", limits: false),
|
"deg" : MTMathAtomFactory.operatorWithName( "deg", limits: false),
|
||||||
|
|
||||||
// Limit operators
|
// Limit operators
|
||||||
"lim" : MTMathAtomFactory.getOperator(withName: "lim", limits: true),
|
"lim" : MTMathAtomFactory.operatorWithName( "lim", limits: true),
|
||||||
"limsup" : MTMathAtomFactory.getOperator(withName: "lim sup", limits: true),
|
"limsup" : MTMathAtomFactory.operatorWithName( "lim sup", limits: true),
|
||||||
"liminf" : MTMathAtomFactory.getOperator(withName: "lim inf", limits: true),
|
"liminf" : MTMathAtomFactory.operatorWithName( "lim inf", limits: true),
|
||||||
"max" : MTMathAtomFactory.getOperator(withName: "max", limits: true),
|
"max" : MTMathAtomFactory.operatorWithName( "max", limits: true),
|
||||||
"min" : MTMathAtomFactory.getOperator(withName: "min", limits: true),
|
"min" : MTMathAtomFactory.operatorWithName( "min", limits: true),
|
||||||
"sup" : MTMathAtomFactory.getOperator(withName: "sup", limits: true),
|
"sup" : MTMathAtomFactory.operatorWithName( "sup", limits: true),
|
||||||
"inf" : MTMathAtomFactory.getOperator(withName: "inf", limits: true),
|
"inf" : MTMathAtomFactory.operatorWithName( "inf", limits: true),
|
||||||
"det" : MTMathAtomFactory.getOperator(withName: "det", limits: true),
|
"det" : MTMathAtomFactory.operatorWithName( "det", limits: true),
|
||||||
"Pr" : MTMathAtomFactory.getOperator(withName: "Pr", limits: true),
|
"Pr" : MTMathAtomFactory.operatorWithName( "Pr", limits: true),
|
||||||
"gcd" : MTMathAtomFactory.getOperator(withName: "gcd", limits: true),
|
"gcd" : MTMathAtomFactory.operatorWithName( "gcd", limits: true),
|
||||||
|
|
||||||
// Large operators
|
// Large operators
|
||||||
"prod" : MTMathAtomFactory.getOperator(withName: "\u{220F}", limits: true),
|
"prod" : MTMathAtomFactory.operatorWithName( "\u{220F}", limits: true),
|
||||||
"coprod" : MTMathAtomFactory.getOperator(withName: "\u{2210}", limits: true),
|
"coprod" : MTMathAtomFactory.operatorWithName( "\u{2210}", limits: true),
|
||||||
"sum" : MTMathAtomFactory.getOperator(withName: "\u{2211}", limits: true),
|
"sum" : MTMathAtomFactory.operatorWithName( "\u{2211}", limits: true),
|
||||||
"int" : MTMathAtomFactory.getOperator(withName: "\u{222B}", limits: false),
|
"int" : MTMathAtomFactory.operatorWithName( "\u{222B}", limits: false),
|
||||||
"oint" : MTMathAtomFactory.getOperator(withName: "\u{222E}", limits: false),
|
"oint" : MTMathAtomFactory.operatorWithName( "\u{222E}", limits: false),
|
||||||
"bigwedge" : MTMathAtomFactory.getOperator(withName: "\u{22C0}", limits: true),
|
"bigwedge" : MTMathAtomFactory.operatorWithName( "\u{22C0}", limits: true),
|
||||||
"bigvee" : MTMathAtomFactory.getOperator(withName: "\u{22C1}", limits: true),
|
"bigvee" : MTMathAtomFactory.operatorWithName( "\u{22C1}", limits: true),
|
||||||
"bigcap" : MTMathAtomFactory.getOperator(withName: "\u{22C2}", limits: true),
|
"bigcap" : MTMathAtomFactory.operatorWithName( "\u{22C2}", limits: true),
|
||||||
"bigcup" : MTMathAtomFactory.getOperator(withName: "\u{22C3}", limits: true),
|
"bigcup" : MTMathAtomFactory.operatorWithName( "\u{22C3}", limits: true),
|
||||||
"bigodot" : MTMathAtomFactory.getOperator(withName: "\u{2A00}", limits: true),
|
"bigodot" : MTMathAtomFactory.operatorWithName( "\u{2A00}", limits: true),
|
||||||
"bigoplus" : MTMathAtomFactory.getOperator(withName: "\u{2A01}", limits: true),
|
"bigoplus" : MTMathAtomFactory.operatorWithName( "\u{2A01}", limits: true),
|
||||||
"bigotimes" : MTMathAtomFactory.getOperator(withName: "\u{2A02}", limits: true),
|
"bigotimes" : MTMathAtomFactory.operatorWithName( "\u{2A02}", limits: true),
|
||||||
"biguplus" : MTMathAtomFactory.getOperator(withName: "\u{2A04}", limits: true),
|
"biguplus" : MTMathAtomFactory.operatorWithName( "\u{2A04}", limits: true),
|
||||||
"bigsqcup" : MTMathAtomFactory.getOperator(withName: "\u{2A06}", limits: true),
|
"bigsqcup" : MTMathAtomFactory.operatorWithName( "\u{2A06}", limits: true),
|
||||||
|
|
||||||
// Latex command characters
|
// Latex command characters
|
||||||
"{" : MTMathAtom(type: .open, value: "{"),
|
"{" : MTMathAtom(type: .open, value: "{"),
|
||||||
@@ -454,6 +454,40 @@ public class MTMathAtomFactory {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static func fontNameForStyle(_ fontStyle:MTFontStyle) -> String {
|
||||||
|
switch fontStyle {
|
||||||
|
case .defaultStyle:
|
||||||
|
return "mathnormal";
|
||||||
|
|
||||||
|
case .roman:
|
||||||
|
return "mathrm";
|
||||||
|
|
||||||
|
case .bold:
|
||||||
|
return "mathbf";
|
||||||
|
|
||||||
|
case .fraktur:
|
||||||
|
return "mathfrak";
|
||||||
|
|
||||||
|
case .caligraphic:
|
||||||
|
return "mathcal";
|
||||||
|
|
||||||
|
case .italic:
|
||||||
|
return "mathit";
|
||||||
|
|
||||||
|
case .sansSerif:
|
||||||
|
return "mathsf";
|
||||||
|
|
||||||
|
case .blackboard:
|
||||||
|
return "mathbb";
|
||||||
|
|
||||||
|
case .typewriter:
|
||||||
|
return "mathtt";
|
||||||
|
|
||||||
|
case .boldItalic:
|
||||||
|
return "bm";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Return an atom for times sign \times or *
|
// Return an atom for times sign \times or *
|
||||||
public static func times() -> MTMathAtom {
|
public static func times() -> MTMathAtom {
|
||||||
return MTMathAtom(type: .binaryOperator, value: UnicodeSymbol.multiplication)
|
return MTMathAtom(type: .binaryOperator, value: UnicodeSymbol.multiplication)
|
||||||
@@ -511,43 +545,41 @@ public class MTMathAtomFactory {
|
|||||||
- Chars with special meaning in latex: ^ _ { } \
|
- Chars with special meaning in latex: ^ _ { } \
|
||||||
All other characters will have a non-nil atom returned.
|
All other characters will have a non-nil atom returned.
|
||||||
*/
|
*/
|
||||||
public static func atom(for char: String) -> MTMathAtom? {
|
public static func atom(forCharacter ch: Character) -> MTMathAtom? {
|
||||||
let atomCharacterSet = CharacterSet(charactersIn: UnicodeScalar(0x21)!...UnicodeScalar(0x7E)!)
|
let chStr = String(ch)
|
||||||
if char.rangeOfCharacter(from: atomCharacterSet) != nil {
|
switch chStr {
|
||||||
switch char {
|
case "\\u{0410}"..."\\u{044F}":
|
||||||
|
return MTMathAtom(type: .ordinary, value: chStr)
|
||||||
|
case _ where ch.utf32Char < 0x0021 || ch.utf32Char > 0x007E:
|
||||||
|
return nil
|
||||||
case "$", "%", "#", "&", "~", "\'", "^", "_", "{", "}", "\\":
|
case "$", "%", "#", "&", "~", "\'", "^", "_", "{", "}", "\\":
|
||||||
return nil
|
return nil
|
||||||
case "(", "[":
|
case "(", "[":
|
||||||
return MTMathAtom(type: .open, value: char)
|
return MTMathAtom(type: .open, value: chStr)
|
||||||
case ")", "]", "!", "?":
|
case ")", "]", "!", "?":
|
||||||
return MTMathAtom(type: .close, value: char)
|
return MTMathAtom(type: .close, value: chStr)
|
||||||
case ",", ";":
|
case ",", ";":
|
||||||
return MTMathAtom(type: .punctuation, value: char)
|
return MTMathAtom(type: .punctuation, value: chStr)
|
||||||
case "=", ">", "<":
|
case "=", ">", "<":
|
||||||
return MTMathAtom(type: .relation, value: char)
|
return MTMathAtom(type: .relation, value: chStr)
|
||||||
case ":":
|
case ":":
|
||||||
// Math colon is ratio. Regular colon is \colon
|
// Math colon is ratio. Regular colon is \colon
|
||||||
return MTMathAtom(type: .relation, value: "\u{2236}")
|
return MTMathAtom(type: .relation, value: "\u{2236}")
|
||||||
case "-":
|
case "-":
|
||||||
return MTMathAtom(type: .binaryOperator, value: "\u{2212}")
|
return MTMathAtom(type: .binaryOperator, value: "\u{2212}")
|
||||||
case "+", "*":
|
case "+", "*":
|
||||||
return MTMathAtom(type: .binaryOperator, value: char)
|
return MTMathAtom(type: .binaryOperator, value: chStr)
|
||||||
case ".", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9":
|
case ".", "0"..."9":
|
||||||
return MTMathAtom(type: .number, value: char)
|
return MTMathAtom(type: .number, value: chStr)
|
||||||
case _ where
|
case "a"..."z", "A"..."Z":
|
||||||
(char.rangeOfCharacter(from: CharacterSet.lowercaseLetters) != nil) ||
|
return MTMathAtom(type: .variable, value: chStr)
|
||||||
(char.rangeOfCharacter(from: CharacterSet.lowercaseLetters) != nil) :
|
|
||||||
return MTMathAtom(type: .variable, value: char)
|
|
||||||
case "\"", "/", "@", "`", "|":
|
case "\"", "/", "@", "`", "|":
|
||||||
return MTMathAtom(type: .ordinary, value: char)
|
return MTMathAtom(type: .ordinary, value: chStr)
|
||||||
default:
|
default:
|
||||||
assert(false, "Unknown Character: \(char)")
|
assertionFailure("Unknown ASCII character '\(ch)'. Should have been handled earlier.")
|
||||||
print("Unknown characters: \(char)")
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns a `MTMathList` with one atom per character in the given string. This function
|
/** Returns a `MTMathList` with one atom per character in the given string. This function
|
||||||
does not do any LaTeX conversion or interpretation. It simply uses `atomForCharacter` to
|
does not do any LaTeX conversion or interpretation. It simply uses `atomForCharacter` to
|
||||||
@@ -556,7 +588,7 @@ public class MTMathAtomFactory {
|
|||||||
let list = MTMathList()
|
let list = MTMathList()
|
||||||
|
|
||||||
for character in string {
|
for character in string {
|
||||||
if let newAtom = atom(for: "\(character)") {
|
if let newAtom = atom(forCharacter: character) {
|
||||||
list.add(newAtom)
|
list.add(newAtom)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -602,14 +634,14 @@ public class MTMathAtomFactory {
|
|||||||
e.g. to define a symbol for "lcm" one can call:
|
e.g. to define a symbol for "lcm" one can call:
|
||||||
`[MTMathAtomFactory addLatexSymbol:@"lcm" value:[MTMathAtomFactory operatorWithName:@"lcm" limits: false)]` */
|
`[MTMathAtomFactory addLatexSymbol:@"lcm" value:[MTMathAtomFactory operatorWithName:@"lcm" limits: false)]` */
|
||||||
|
|
||||||
public static func define(latexSymbol name: String, value: MTMathAtom) {
|
public static func add(latexSymbol name: String, value: MTMathAtom) {
|
||||||
Self.sharedInstance.supportedLatexSymbols[name] = value
|
Self.sharedInstance.supportedLatexSymbols[name] = value
|
||||||
Self.sharedInstance.textToLatexSymbolName[value.nucleus] = name
|
Self.sharedInstance.textToLatexSymbolName[value.nucleus] = name
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns a large opertor for the given name. If limits is true, limits are set up on
|
/** Returns a large opertor for the given name. If limits is true, limits are set up on
|
||||||
the operator and displyed differently. */
|
the operator and displyed differently. */
|
||||||
public static func getOperator(withName name: String, limits: Bool) -> MTLargeOperator {
|
public static func operatorWithName(_ name: String, limits: Bool) -> MTLargeOperator {
|
||||||
return MTLargeOperator(value: name, limits: limits)
|
return MTLargeOperator(value: name, limits: limits)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -617,7 +649,7 @@ public class MTMathAtomFactory {
|
|||||||
such as `grave`, `hat` etc. If the name is not a recognized accent name, this
|
such as `grave`, `hat` etc. If the name is not a recognized accent name, this
|
||||||
returns nil. The `innerList` of the returned `MTAccent` is nil.
|
returns nil. The `innerList` of the returned `MTAccent` is nil.
|
||||||
*/
|
*/
|
||||||
public static func getAccent(withName name: String) -> MTAccent? {
|
public static func accent(withName name: String) -> MTAccent? {
|
||||||
if let accentValue = Self.accents[name] {
|
if let accentValue = Self.accents[name] {
|
||||||
return MTAccent(value: accentValue)
|
return MTAccent(value: accentValue)
|
||||||
}
|
}
|
||||||
@@ -626,7 +658,7 @@ public class MTMathAtomFactory {
|
|||||||
|
|
||||||
/** Returns the accent name for the given accent. This is the reverse of the above
|
/** Returns the accent name for the given accent. This is the reverse of the above
|
||||||
function. */
|
function. */
|
||||||
public static func getName(of accent: MTAccent) -> String? {
|
public static func accentName(_ accent: MTAccent) -> String? {
|
||||||
return Self.sharedInstance.accentValueToName[accent.nucleus]
|
return Self.sharedInstance.accentValueToName[accent.nucleus]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -681,7 +713,7 @@ public class MTMathAtomFactory {
|
|||||||
@note The reason this function returns a `MTMathAtom` and not a `MTMathTable` is because some
|
@note The reason this function returns a `MTMathAtom` and not a `MTMathTable` is because some
|
||||||
matrix environments are have builtin delimiters added to the table and hence are returned as inner atoms.
|
matrix environments are have builtin delimiters added to the table and hence are returned as inner atoms.
|
||||||
*/
|
*/
|
||||||
public static func table(withEnvironment env: String?, rows: [[MTMathList]]) -> MTMathAtom? {
|
public static func table(withEnvironment env: String?, rows: [[MTMathList]], error:inout NSError?) -> MTMathAtom? {
|
||||||
let table = MTMathTable(environment: env)
|
let table = MTMathTable(environment: env)
|
||||||
|
|
||||||
for i in 0..<rows.count {
|
for i in 0..<rows.count {
|
||||||
@@ -703,12 +735,12 @@ public class MTMathAtomFactory {
|
|||||||
if env == nil {
|
if env == nil {
|
||||||
table.interColumnSpacing = 0
|
table.interColumnSpacing = 0
|
||||||
table.interRowAdditionalSpacing = 1
|
table.interRowAdditionalSpacing = 1
|
||||||
for i in 0..<table.numberOfCols() {
|
for i in 0..<table.numColumns {
|
||||||
table.set(alignment: .left, forCol: i)
|
table.set(alignment: .left, forColumn: i)
|
||||||
}
|
}
|
||||||
return table
|
return table
|
||||||
} else {
|
} else if let env = env {
|
||||||
if let delims = matrixEnvs[env!] {
|
if let delims = matrixEnvs[env] {
|
||||||
table.environment = "matrix"
|
table.environment = "matrix"
|
||||||
table.interRowAdditionalSpacing = 0
|
table.interRowAdditionalSpacing = 0
|
||||||
table.interColumnSpacing = 18
|
table.interColumnSpacing = 18
|
||||||
@@ -731,8 +763,11 @@ public class MTMathAtomFactory {
|
|||||||
return table
|
return table
|
||||||
}
|
}
|
||||||
} else if env == "eqalign" || env == "split" || env == "aligned" {
|
} else if env == "eqalign" || env == "split" || env == "aligned" {
|
||||||
if table.numberOfCols() != 2 {
|
if table.numColumns != 2 {
|
||||||
print("\(env!) environment can only have 2 columns")
|
let message = "\(env) environment can only have 2 columns"
|
||||||
|
if error != nil {
|
||||||
|
error = NSError(domain: MTParseError, code: MTParseErrors.invalidNumColumns.rawValue, userInfo: [NSLocalizedDescriptionKey:message])
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -747,47 +782,56 @@ public class MTMathAtomFactory {
|
|||||||
table.interRowAdditionalSpacing = 1
|
table.interRowAdditionalSpacing = 1
|
||||||
table.interColumnSpacing = 0
|
table.interColumnSpacing = 0
|
||||||
|
|
||||||
table.set(alignment: .right, forCol: 0)
|
table.set(alignment: .right, forColumn: 0)
|
||||||
table.set(alignment: .left, forCol: 1)
|
table.set(alignment: .left, forColumn: 1)
|
||||||
|
|
||||||
return table
|
return table
|
||||||
} else if env == "displaylines" || env == "gather" {
|
} else if env == "displaylines" || env == "gather" {
|
||||||
if table.numberOfCols() != 1 {
|
if table.numColumns != 1 {
|
||||||
print("\(env!) environment can only have 1 columns")
|
let message = "\(env) environment can only have 1 column"
|
||||||
|
if error != nil {
|
||||||
|
error = NSError(domain: MTParseError, code: MTParseErrors.invalidNumColumns.rawValue, userInfo: [NSLocalizedDescriptionKey:message])
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
table.interRowAdditionalSpacing = 1
|
table.interRowAdditionalSpacing = 1
|
||||||
table.interColumnSpacing = 0
|
table.interColumnSpacing = 0
|
||||||
|
|
||||||
table.set(alignment: .center, forCol: 0)
|
table.set(alignment: .center, forColumn: 0)
|
||||||
|
|
||||||
return table
|
return table
|
||||||
} else if env == "eqnarray" {
|
} else if env == "eqnarray" {
|
||||||
if table.numberOfCols() != 3 {
|
if table.numColumns != 3 {
|
||||||
print("\(env!) environment can only have 3 columns")
|
let message = "\(env) environment can only have 3 columns"
|
||||||
|
if error != nil {
|
||||||
|
error = NSError(domain: MTParseError, code: MTParseErrors.invalidNumColumns.rawValue, userInfo: [NSLocalizedDescriptionKey:message])
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
table.interRowAdditionalSpacing = 1
|
table.interRowAdditionalSpacing = 1
|
||||||
table.interColumnSpacing = 18
|
table.interColumnSpacing = 18
|
||||||
|
|
||||||
table.set(alignment: .right, forCol: 0)
|
table.set(alignment: .right, forColumn: 0)
|
||||||
table.set(alignment: .center, forCol: 1)
|
table.set(alignment: .center, forColumn: 1)
|
||||||
table.set(alignment: .left, forCol: 2)
|
table.set(alignment: .left, forColumn: 2)
|
||||||
|
|
||||||
return table
|
return table
|
||||||
} else if env == "cases" {
|
} else if env == "cases" {
|
||||||
if table.numberOfCols() != 2 {
|
if table.numColumns != 2 {
|
||||||
print("\(env!) environment can only have 2 columns")
|
let message = "cases environment can only have 2 columns"
|
||||||
|
if error != nil {
|
||||||
|
error = NSError(domain: MTParseError, code: MTParseErrors.invalidNumColumns.rawValue, userInfo: [NSLocalizedDescriptionKey:message])
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
table.interRowAdditionalSpacing = 0
|
table.interRowAdditionalSpacing = 0
|
||||||
table.interColumnSpacing = 18
|
table.interColumnSpacing = 18
|
||||||
|
|
||||||
table.set(alignment: .left, forCol: 0)
|
table.set(alignment: .left, forColumn: 0)
|
||||||
table.set(alignment: .left, forCol: 1)
|
table.set(alignment: .left, forColumn: 1)
|
||||||
|
|
||||||
let style = MTMathStyle(style: .text)
|
let style = MTMathStyle(style: .text)
|
||||||
|
|
||||||
@@ -807,9 +851,13 @@ public class MTMathAtomFactory {
|
|||||||
|
|
||||||
return inner
|
return inner
|
||||||
} else {
|
} else {
|
||||||
print("Unknown table environment")
|
let message = "Unknown environment \(env)"
|
||||||
|
if error != nil {
|
||||||
|
error = NSError(domain: MTParseError, code: MTParseErrors.invalidNumColumns.rawValue, userInfo: [NSLocalizedDescriptionKey:message])
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ public enum MTFontStyle:Int {
|
|||||||
boldItalic
|
boldItalic
|
||||||
}
|
}
|
||||||
|
|
||||||
public class MTMathAtom: Any, CustomStringConvertible {
|
public class MTMathAtom: CustomStringConvertible {
|
||||||
public var type: MTMathAtomType
|
public var type: MTMathAtomType
|
||||||
public var subScript: MTMathList?
|
public var subScript: MTMathList?
|
||||||
public var superScript: MTMathList?
|
public var superScript: MTMathList?
|
||||||
@@ -271,10 +271,10 @@ public class MTFraction: MTMathAtom {
|
|||||||
|
|
||||||
public class MTRadical: MTMathAtom {
|
public class MTRadical: MTMathAtom {
|
||||||
// Under the roof
|
// Under the roof
|
||||||
var radicand: MTMathList? = MTMathList()
|
public var radicand: MTMathList? = MTMathList()
|
||||||
|
|
||||||
// Value on radical sign
|
// Value on radical sign
|
||||||
var degree: MTMathList?
|
public var degree: MTMathList?
|
||||||
|
|
||||||
convenience init() {
|
convenience init() {
|
||||||
self.init(type: .radical, value: "")
|
self.init(type: .radical, value: "")
|
||||||
@@ -312,7 +312,7 @@ public class MTRadical: MTMathAtom {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public class MTLargeOperator: MTMathAtom {
|
public class MTLargeOperator: MTMathAtom {
|
||||||
var limits: Bool = false
|
public var limits: Bool = false
|
||||||
|
|
||||||
convenience init(value: String, limits: Bool = false) {
|
convenience init(value: String, limits: Bool = false) {
|
||||||
self.init(type: .largeOperator, value: value)
|
self.init(type: .largeOperator, value: value)
|
||||||
@@ -323,15 +323,15 @@ public class MTLargeOperator: MTMathAtom {
|
|||||||
// MARK: - MTInner
|
// MARK: - MTInner
|
||||||
|
|
||||||
public class MTInner: MTMathAtom {
|
public class MTInner: MTMathAtom {
|
||||||
var innerList: MTMathList?
|
public var innerList: MTMathList?
|
||||||
var leftBoundary: MTMathAtom? {
|
public var leftBoundary: MTMathAtom? {
|
||||||
didSet {
|
didSet {
|
||||||
if leftBoundary != nil && leftBoundary!.type != .boundary {
|
if leftBoundary != nil && leftBoundary!.type != .boundary {
|
||||||
assertionFailure("Left boundary must be of type .boundary")
|
assertionFailure("Left boundary must be of type .boundary")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var rightBoundary: MTMathAtom? {
|
public var rightBoundary: MTMathAtom? {
|
||||||
didSet {
|
didSet {
|
||||||
if rightBoundary != nil && rightBoundary!.type != .boundary {
|
if rightBoundary != nil && rightBoundary!.type != .boundary {
|
||||||
assertionFailure("Right boundary must be of type .boundary")
|
assertionFailure("Right boundary must be of type .boundary")
|
||||||
@@ -383,7 +383,7 @@ public class MTInner: MTMathAtom {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public class MTOverLine: MTMathAtom {
|
public class MTOverLine: MTMathAtom {
|
||||||
var innerList: MTMathList?
|
public var innerList: MTMathList?
|
||||||
|
|
||||||
override public var finalized: MTMathAtom {
|
override public var finalized: MTMathAtom {
|
||||||
let finalized: MTOverLine = super.finalized as! MTOverLine
|
let finalized: MTOverLine = super.finalized as! MTOverLine
|
||||||
@@ -399,7 +399,7 @@ public class MTOverLine: MTMathAtom {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public class MTUnderLine: MTMathAtom {
|
public class MTUnderLine: MTMathAtom {
|
||||||
var innerList: MTMathList?
|
public var innerList: MTMathList?
|
||||||
|
|
||||||
override public var finalized: MTMathAtom {
|
override public var finalized: MTMathAtom {
|
||||||
let finalized: MTUnderLine = super.finalized as! MTUnderLine
|
let finalized: MTUnderLine = super.finalized as! MTUnderLine
|
||||||
@@ -415,7 +415,7 @@ public class MTUnderLine: MTMathAtom {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public class MTAccent: MTMathAtom {
|
public class MTAccent: MTMathAtom {
|
||||||
var innerList: MTMathList?
|
public var innerList: MTMathList?
|
||||||
|
|
||||||
override public var finalized: MTMathAtom {
|
override public var finalized: MTMathAtom {
|
||||||
let finalized: MTAccent = super.finalized as! MTAccent
|
let finalized: MTAccent = super.finalized as! MTAccent
|
||||||
@@ -431,7 +431,7 @@ public class MTAccent: MTMathAtom {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public class MTMathSpace: MTMathAtom {
|
public class MTMathSpace: MTMathAtom {
|
||||||
var space: CGFloat = 0
|
public var space: CGFloat = 0
|
||||||
|
|
||||||
convenience init(space: CGFloat) {
|
convenience init(space: CGFloat) {
|
||||||
self.init(type: .space, value: "")
|
self.init(type: .space, value: "")
|
||||||
@@ -460,10 +460,10 @@ public enum MTLineStyle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public class MTMathStyle: MTMathAtom {
|
public class MTMathStyle: MTMathAtom {
|
||||||
var style: MTLineStyle = .display
|
public var style: MTLineStyle = .display
|
||||||
|
|
||||||
convenience init(style: MTLineStyle = .display) {
|
convenience init(style: MTLineStyle = .display) {
|
||||||
self.init(type: .space, value: "")
|
self.init(type: .style, value: "")
|
||||||
self.style = style
|
self.style = style
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -517,14 +517,14 @@ public enum MTColumnAlignment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public class MTMathTable: MTMathAtom {
|
public class MTMathTable: MTMathAtom {
|
||||||
var alignments = [MTColumnAlignment]()
|
public var alignments = [MTColumnAlignment]()
|
||||||
var cells = [[MTMathList]]()
|
public var cells = [[MTMathList]]()
|
||||||
|
|
||||||
var environment: String?
|
public var environment: String?
|
||||||
var interColumnSpacing: CGFloat = 0
|
public var interColumnSpacing: CGFloat = 0
|
||||||
var interRowAdditionalSpacing: CGFloat = 0
|
public var interRowAdditionalSpacing: CGFloat = 0
|
||||||
var numColumns = 0
|
// public var numColumns = 0
|
||||||
var numRows = 0
|
// public var numRows = 0
|
||||||
|
|
||||||
override public var finalized: MTMathAtom {
|
override public var finalized: MTMathAtom {
|
||||||
let finalized: MTMathTable = super.finalized as! MTMathTable
|
let finalized: MTMathTable = super.finalized as! MTMathTable
|
||||||
@@ -543,22 +543,22 @@ public class MTMathTable: MTMathAtom {
|
|||||||
self.environment = environment
|
self.environment = environment
|
||||||
}
|
}
|
||||||
|
|
||||||
func set(cell list: MTMathList, forRow row:Int, column:Int) {
|
public func set(cell list: MTMathList, forRow row:Int, column:Int) {
|
||||||
if self.cells.count <= row {
|
if self.cells.count <= row {
|
||||||
for _ in self.cells.count...row {
|
for _ in self.cells.count...row {
|
||||||
self.cells.append([])
|
self.cells.append([])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var rowArray = self.cells[row]
|
let rows = self.cells[row].count
|
||||||
if rowArray.count <= column {
|
if rows <= column {
|
||||||
for _ in rowArray.count...column {
|
for _ in rows...column {
|
||||||
rowArray.append(MTMathList())
|
self.cells[row].append(MTMathList())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rowArray[column] = list
|
self.cells[row][column] = list
|
||||||
}
|
}
|
||||||
|
|
||||||
func set(alignment: MTColumnAlignment, forCol col: Int) {
|
public func set(alignment: MTColumnAlignment, forColumn col: Int) {
|
||||||
if self.alignments.count <= col {
|
if self.alignments.count <= col {
|
||||||
for _ in self.alignments.count...col {
|
for _ in self.alignments.count...col {
|
||||||
self.alignments.append(MTColumnAlignment.center)
|
self.alignments.append(MTColumnAlignment.center)
|
||||||
@@ -568,7 +568,7 @@ public class MTMathTable: MTMathAtom {
|
|||||||
self.alignments[col] = alignment
|
self.alignments[col] = alignment
|
||||||
}
|
}
|
||||||
|
|
||||||
func getAlignmentOf(col: Int) -> MTColumnAlignment {
|
public func get(alignmentForColumn col: Int) -> MTColumnAlignment {
|
||||||
if self.alignments.count <= col {
|
if self.alignments.count <= col {
|
||||||
return MTColumnAlignment.center
|
return MTColumnAlignment.center
|
||||||
} else {
|
} else {
|
||||||
@@ -576,7 +576,7 @@ public class MTMathTable: MTMathAtom {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func numberOfCols() -> Int {
|
public var numColumns: Int {
|
||||||
var numberOfCols = 0
|
var numberOfCols = 0
|
||||||
|
|
||||||
for row in self.cells {
|
for row in self.cells {
|
||||||
@@ -586,7 +586,7 @@ public class MTMathTable: MTMathAtom {
|
|||||||
return numberOfCols
|
return numberOfCols
|
||||||
}
|
}
|
||||||
|
|
||||||
func numberOfRows() -> Int {
|
public var numRows: Int {
|
||||||
return self.cells.count
|
return self.cells.count
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -92,6 +92,7 @@ public class MTMathListBuilder {
|
|||||||
]
|
]
|
||||||
|
|
||||||
init(string: String) {
|
init(string: String) {
|
||||||
|
self.error = nil
|
||||||
self.string = string
|
self.string = string
|
||||||
self.currentCharIndex = string.startIndex
|
self.currentCharIndex = string.startIndex
|
||||||
self.currentFontStyle = .defaultStyle
|
self.currentFontStyle = .defaultStyle
|
||||||
@@ -115,31 +116,39 @@ public class MTMathListBuilder {
|
|||||||
return builder.build()
|
return builder.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func build(fromString string: String, error:inout NSError?) -> MTMathList? {
|
public static func build(fromString string: String, error:inout NSError) -> MTMathList? {
|
||||||
let builder = MTMathListBuilder(string: string)
|
let builder = MTMathListBuilder(string: string)
|
||||||
let output = builder.build()
|
let output = builder.build()
|
||||||
if builder.error != nil {
|
if builder.error != nil {
|
||||||
if error != nil {
|
error = builder.error!
|
||||||
error = builder.error
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
|
} else {
|
||||||
|
error = NSError()
|
||||||
}
|
}
|
||||||
return output
|
return output
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func mathListToString(_ ml: MTMathList?) -> String {
|
public static func mathListToString(_ ml: MTMathList?) -> String {
|
||||||
var output = ""
|
var str = ""
|
||||||
|
var currentfontStyle = MTFontStyle.defaultStyle
|
||||||
if let atomList = ml {
|
if let atomList = ml {
|
||||||
for atom in atomList.atoms {
|
for atom in atomList.atoms {
|
||||||
switch atom.type {
|
if currentfontStyle != atom.fontStyle {
|
||||||
case .fraction:
|
if currentfontStyle != .defaultStyle {
|
||||||
|
str += "}"
|
||||||
|
}
|
||||||
|
if atom.fontStyle != .defaultStyle {
|
||||||
|
let fontStyleName = MTMathAtomFactory.fontNameForStyle(atom.fontStyle)
|
||||||
|
str += "\\\(fontStyleName){"
|
||||||
|
}
|
||||||
|
currentfontStyle = atom.fontStyle
|
||||||
|
}
|
||||||
|
if atom.type == .fraction {
|
||||||
if let frac = atom as? MTFraction {
|
if let frac = atom as? MTFraction {
|
||||||
if frac.hasRule {
|
if frac.hasRule {
|
||||||
output += "\\frac{\(mathListToString(frac.numerator!))}{\(mathListToString(frac.denominator!))}"
|
str += "\\frac{\(mathListToString(frac.numerator!))}{\(mathListToString(frac.denominator!))}"
|
||||||
} else {
|
} else {
|
||||||
var command: String? = nil
|
var command: String? = nil
|
||||||
|
|
||||||
if frac.leftDelimiter == nil && frac.rightDelimiter == nil {
|
if frac.leftDelimiter == nil && frac.rightDelimiter == nil {
|
||||||
command = "atop"
|
command = "atop"
|
||||||
} else if frac.leftDelimiter == "(" && frac.rightDelimiter == ")" {
|
} else if frac.leftDelimiter == "(" && frac.rightDelimiter == ")" {
|
||||||
@@ -151,51 +160,47 @@ public class MTMathListBuilder {
|
|||||||
} else {
|
} else {
|
||||||
command = "atopwithdelims\(frac.leftDelimiter!)\(frac.rightDelimiter!)"
|
command = "atopwithdelims\(frac.leftDelimiter!)\(frac.rightDelimiter!)"
|
||||||
}
|
}
|
||||||
|
str += "{\(mathListToString(frac.numerator!)) \\\(command!) \(mathListToString(frac.denominator!))}"
|
||||||
output += "{\(mathListToString(frac.numerator!)) \\\(command!) \(mathListToString(frac.denominator!))}"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break
|
} else if atom.type == .radical {
|
||||||
case .radical:
|
str += "\\sqrt"
|
||||||
output += "\\sqrt"
|
|
||||||
if let rad = atom as? MTRadical {
|
if let rad = atom as? MTRadical {
|
||||||
if rad.degree != nil {
|
if rad.degree != nil {
|
||||||
output += "[\(mathListToString(rad.degree!))]"
|
str += "[\(mathListToString(rad.degree!))]"
|
||||||
}
|
}
|
||||||
output += "{\(mathListToString(rad.radicand!))}"
|
str += "{\(mathListToString(rad.radicand!))}"
|
||||||
}
|
}
|
||||||
break
|
} else if atom.type == .inner {
|
||||||
case .inner:
|
|
||||||
if let inner = atom as? MTInner {
|
if let inner = atom as? MTInner {
|
||||||
if inner.leftBoundary != nil || inner.rightBoundary != nil {
|
if inner.leftBoundary != nil || inner.rightBoundary != nil {
|
||||||
if inner.leftBoundary != nil {
|
if inner.leftBoundary != nil {
|
||||||
output += "\\left\(delimToString(delim: inner.leftBoundary!))"
|
str += "\\left\(delimToString(delim: inner.leftBoundary!)) "
|
||||||
} else {
|
} else {
|
||||||
output += "\\left. "
|
str += "\\left. "
|
||||||
}
|
}
|
||||||
|
|
||||||
output += mathListToString(inner.innerList!)
|
str += mathListToString(inner.innerList!)
|
||||||
|
|
||||||
if inner.rightBoundary != nil {
|
if inner.rightBoundary != nil {
|
||||||
output += "\\right\(delimToString(delim: inner.rightBoundary!))"
|
str += "\\right\(delimToString(delim: inner.rightBoundary!)) "
|
||||||
} else {
|
} else {
|
||||||
output += "\\right. "
|
str += "\\right. "
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
output += "{\(mathListToString(inner.innerList!))}"
|
str += "{\(mathListToString(inner.innerList!))}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break
|
} else if atom.type == .table {
|
||||||
case .table:
|
|
||||||
if let table = atom as? MTMathTable {
|
if let table = atom as? MTMathTable {
|
||||||
if table.environment != nil {
|
if table.environment != nil {
|
||||||
output += "\\begin{\(table.environment!)}"
|
str += "\\begin{\(table.environment!)}"
|
||||||
}
|
}
|
||||||
|
|
||||||
for i in 0..<table.numberOfRows() {
|
for i in 0..<table.numRows {
|
||||||
for j in 0..<table.cells[i].count {
|
let row = table.cells[i]
|
||||||
let cell = table.cells[i][j]
|
for j in 0..<row.count {
|
||||||
|
let cell = row[j]
|
||||||
if table.environment == "matrix" {
|
if table.environment == "matrix" {
|
||||||
if cell.atoms.count >= 1 && cell.atoms[0].type == .style {
|
if cell.atoms.count >= 1 && cell.atoms[0].type == .style {
|
||||||
// remove first atom
|
// remove first atom
|
||||||
@@ -209,82 +214,90 @@ public class MTMathListBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
output += mathListToString(cell)
|
str += mathListToString(cell)
|
||||||
|
|
||||||
if j < table.cells[i].count {
|
if j < row.count - 1 {
|
||||||
output += "&"
|
str += "&"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if i < table.numberOfRows() - 1 {
|
if i < table.numRows - 1 {
|
||||||
output += "\\\\ "
|
str += "\\\\ "
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if table.environment != nil {
|
if table.environment != nil {
|
||||||
output += "\\end{\(table.environment!)}"
|
str += "\\end{\(table.environment!)}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break
|
} else if atom.type == .overline {
|
||||||
case .overline:
|
|
||||||
if let overline = atom as? MTOverLine {
|
if let overline = atom as? MTOverLine {
|
||||||
output += "\\overline"
|
str += "\\overline"
|
||||||
output += "{\(mathListToString(overline.innerList!))}"
|
str += "{\(mathListToString(overline.innerList!))}"
|
||||||
}
|
}
|
||||||
break
|
} else if atom.type == .underline {
|
||||||
case .underline:
|
|
||||||
if let underline = atom as? MTUnderLine {
|
if let underline = atom as? MTUnderLine {
|
||||||
output += "\\underline"
|
str += "\\underline"
|
||||||
output += "{\(mathListToString(underline.innerList!))}"
|
str += "{\(mathListToString(underline.innerList!))}"
|
||||||
}
|
}
|
||||||
break
|
} else if atom.type == .accent {
|
||||||
case .accent:
|
|
||||||
if let accent = atom as? MTAccent {
|
if let accent = atom as? MTAccent {
|
||||||
output += "\\\(MTMathAtomFactory.getName(of: accent)!){\(mathListToString(accent.innerList!))}"
|
str += "\\\(MTMathAtomFactory.accentName(accent)!){\(mathListToString(accent.innerList!))}"
|
||||||
}
|
}
|
||||||
break
|
} else if atom.type == .largeOperator {
|
||||||
case .space:
|
let op = atom as! MTLargeOperator
|
||||||
|
let command = MTMathAtomFactory.latexSymbolName(for: atom)
|
||||||
|
let originalOp = MTMathAtomFactory.atom(forLatexSymbol: command!) as! MTLargeOperator
|
||||||
|
str += "\\\(command!) "
|
||||||
|
if originalOp.limits != op.limits {
|
||||||
|
if op.limits {
|
||||||
|
str += "\\limits "
|
||||||
|
} else {
|
||||||
|
str += "\\nolimits "
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if atom.type == .space {
|
||||||
if let space = atom as? MTMathSpace {
|
if let space = atom as? MTMathSpace {
|
||||||
if let command = MTMathListBuilder.spaceToCommands[space.space] {
|
if let command = MTMathListBuilder.spaceToCommands[space.space] {
|
||||||
output += "\\\(command)"
|
str += "\\\(command) "
|
||||||
} else {
|
} else {
|
||||||
output += String.init(format: "\\mkern%.1fmu", space.space)
|
str += String(format: "\\mkern%.1fmu", space.space)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break
|
} else if atom.type == .style {
|
||||||
case .style:
|
|
||||||
if let style = atom as? MTMathStyle {
|
if let style = atom as? MTMathStyle {
|
||||||
if let command = MTMathListBuilder.styleToCommands[style.style] {
|
if let command = MTMathListBuilder.styleToCommands[style.style] {
|
||||||
output += "\\\(command)"
|
str += "\\\(command) "
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break
|
} else if atom.nucleus.isEmpty {
|
||||||
default:
|
str += "{}"
|
||||||
if atom.nucleus.count == 0 {
|
|
||||||
output += "{}"
|
|
||||||
} else if atom.nucleus == "\u{2236}" {
|
} else if atom.nucleus == "\u{2236}" {
|
||||||
output += ":"
|
// math colon
|
||||||
|
str += ":"
|
||||||
} else if atom.nucleus == "\u{2212}" {
|
} else if atom.nucleus == "\u{2212}" {
|
||||||
output += "-"
|
// math minus
|
||||||
|
str += "-"
|
||||||
} else {
|
} else {
|
||||||
if let command = MTMathAtomFactory.latexSymbolName(for: atom) {
|
if let command = MTMathAtomFactory.latexSymbolName(for: atom) {
|
||||||
output += "\\\(command)"
|
str += "\\\(command) "
|
||||||
} else {
|
} else {
|
||||||
output += "\(atom.nucleus)"
|
str += "\(atom.nucleus)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if atom.superScript != nil {
|
if atom.superScript != nil {
|
||||||
output += "^{\(mathListToString(atom.superScript!))}"
|
str += "^{\(mathListToString(atom.superScript!))}"
|
||||||
}
|
}
|
||||||
|
|
||||||
if atom.subScript != nil {
|
if atom.subScript != nil {
|
||||||
output += "_{\(mathListToString(atom.subScript!))}"
|
str += "_{\(mathListToString(atom.subScript!))}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return output
|
if currentfontStyle != .defaultStyle {
|
||||||
|
str += "}"
|
||||||
|
}
|
||||||
|
return str
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func delimToString(delim: MTMathAtom) -> String {
|
public static func delimToString(delim: MTMathAtom) -> String {
|
||||||
@@ -302,15 +315,12 @@ public class MTMathListBuilder {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func getNextCharacter() -> Character? {
|
func getNextCharacter() -> Character {
|
||||||
assert(self.hasCharacters, "Retrieving character at index \(self.currentCharIndex) beyond length \(self.string.count)")
|
assert(self.hasCharacters, "Retrieving character at index \(self.currentCharIndex) beyond length \(self.string.count)")
|
||||||
if self.hasCharacters {
|
|
||||||
let ch = string[currentCharIndex]
|
let ch = string[currentCharIndex]
|
||||||
currentCharIndex = string.index(after: currentCharIndex)
|
currentCharIndex = string.index(after: currentCharIndex)
|
||||||
return ch
|
return ch
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func unlookCharacter() {
|
func unlookCharacter() {
|
||||||
assert(currentCharIndex > string.startIndex, "Unlooking when at the first character.")
|
assert(currentCharIndex > string.startIndex, "Unlooking when at the first character.")
|
||||||
@@ -322,10 +332,10 @@ public class MTMathListBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func buildInternal(_ oneCharOnly: Bool) -> MTMathList? {
|
func buildInternal(_ oneCharOnly: Bool) -> MTMathList? {
|
||||||
return self.buildInternal(oneCharOnly, stop: nil)
|
return self.buildInternal(oneCharOnly, stopChar: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildInternal(_ oneCharOnly: Bool, stop: Character?) -> MTMathList? {
|
func buildInternal(_ oneCharOnly: Bool, stopChar stop: Character?) -> MTMathList? {
|
||||||
let list = MTMathList()
|
let list = MTMathList()
|
||||||
assert(!(oneCharOnly && stop != nil), "Cannot set both oneCharOnly and stopChar.")
|
assert(!(oneCharOnly && stop != nil), "Cannot set both oneCharOnly and stopChar.")
|
||||||
var prevAtom: MTMathAtom? = nil
|
var prevAtom: MTMathAtom? = nil
|
||||||
@@ -333,7 +343,7 @@ public class MTMathListBuilder {
|
|||||||
if error != nil { return nil }
|
if error != nil { return nil }
|
||||||
|
|
||||||
var atom: MTMathAtom? = nil
|
var atom: MTMathAtom? = nil
|
||||||
let char = self.getNextCharacter()!
|
let char = self.getNextCharacter()
|
||||||
|
|
||||||
if oneCharOnly {
|
if oneCharOnly {
|
||||||
if char == "^" || char == "}" || char == "_" || char == "&" {
|
if char == "^" || char == "}" || char == "_" || char == "&" {
|
||||||
@@ -342,7 +352,7 @@ public class MTMathListBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if stop != nil && char == stop {
|
if stop != nil && char == stop! {
|
||||||
return list
|
return list
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -355,7 +365,7 @@ public class MTMathListBuilder {
|
|||||||
list.add(prevAtom!)
|
list.add(prevAtom!)
|
||||||
}
|
}
|
||||||
|
|
||||||
prevAtom!.setSuperScript(self.buildInternal(true))
|
prevAtom!.superScript = self.buildInternal(true)
|
||||||
continue
|
continue
|
||||||
} else if char == "_" {
|
} else if char == "_" {
|
||||||
assert(!oneCharOnly, "This should have been handled before")
|
assert(!oneCharOnly, "This should have been handled before")
|
||||||
@@ -365,46 +375,43 @@ public class MTMathListBuilder {
|
|||||||
prevAtom = MTMathAtom(type: .ordinary, value: "")
|
prevAtom = MTMathAtom(type: .ordinary, value: "")
|
||||||
list.add(prevAtom!)
|
list.add(prevAtom!)
|
||||||
}
|
}
|
||||||
prevAtom!.setSubScript(self.buildInternal(true))
|
prevAtom!.subScript = self.buildInternal(true)
|
||||||
continue
|
continue
|
||||||
} else if char == "{" {
|
} else if char == "{" {
|
||||||
// this puts us in a recursive routine, and sets oneCharOnly to false and no stop character
|
// this puts us in a recursive routine, and sets oneCharOnly to false and no stop character
|
||||||
if let subList = self.buildInternal(false, stop: "}") {
|
let subList = self.buildInternal(false, stopChar: "}")
|
||||||
prevAtom = subList.atoms.last
|
prevAtom = subList!.atoms.last
|
||||||
list.append(subList)
|
list.append(subList!)
|
||||||
if oneCharOnly {
|
if oneCharOnly {
|
||||||
return list
|
return list
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
} else {
|
|
||||||
print("open brackets but inner...")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
} else if char == "}" {
|
} else if char == "}" {
|
||||||
assert(!oneCharOnly, "This should have been handled before")
|
assert(!oneCharOnly, "This should have been handled before")
|
||||||
assert(stop == nil, "This should have been handled before")
|
assert(stop == nil, "This should have been handled before")
|
||||||
// We encountered a closing brace when there is no stop set, that means there was no
|
// We encountered a closing brace when there is no stop set, that means there was no
|
||||||
// corresponding opening brace.
|
// corresponding opening brace.
|
||||||
print("Mismatched braces")
|
let errorMessage = "Mismatched braces."
|
||||||
|
self.setError(.mismatchBraces, message:errorMessage)
|
||||||
return nil
|
return nil
|
||||||
} else if char == "\\" {
|
} else if char == "\\" {
|
||||||
let command = readCommand()
|
let command = readCommand()
|
||||||
let done = stopCommand(command, list:list, stop:stop)
|
let done = stopCommand(command, list:list, stopChar:stop)
|
||||||
if done != nil {
|
if done != nil {
|
||||||
return done
|
return done
|
||||||
} else if error != nil {
|
} else if error != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if self.applyModifier(command, atom:prevAtom) {
|
if self.applyModifier(command, atom:prevAtom) {
|
||||||
continue;
|
continue
|
||||||
}
|
}
|
||||||
let fontStyle = MTMathAtomFactory.fontStyleWithName(command)
|
|
||||||
if fontStyle != nil {
|
if let fontStyle = MTMathAtomFactory.fontStyleWithName(command) {
|
||||||
let oldSpacesAllowed = spacesAllowed
|
let oldSpacesAllowed = spacesAllowed
|
||||||
// Text has special consideration where it allows spaces without escaping.
|
// Text has special consideration where it allows spaces without escaping.
|
||||||
spacesAllowed = command == "text"
|
spacesAllowed = command == "text"
|
||||||
let oldFontStyle = currentFontStyle
|
let oldFontStyle = currentFontStyle
|
||||||
currentFontStyle = fontStyle!
|
currentFontStyle = fontStyle
|
||||||
let sublist = self.buildInternal(true)!
|
let sublist = self.buildInternal(true)!
|
||||||
// Restore the font style.
|
// Restore the font style.
|
||||||
currentFontStyle = oldFontStyle
|
currentFontStyle = oldFontStyle
|
||||||
@@ -431,27 +438,20 @@ public class MTMathListBuilder {
|
|||||||
if self.currentEnv != nil {
|
if self.currentEnv != nil {
|
||||||
return list
|
return list
|
||||||
} else {
|
} else {
|
||||||
if let table = self.buildTable(env: nil, firstList: list, isRow: false) {
|
let table = self.buildTable(env: nil, firstList: list, isRow: false)
|
||||||
return MTMathList(atom: table)
|
return MTMathList(atom: table!)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else if spacesAllowed && char == " " {
|
} else if spacesAllowed && char == " " {
|
||||||
atom = MTMathAtomFactory.atom(forLatexSymbol: " ")
|
atom = MTMathAtomFactory.atom(forLatexSymbol: " ")
|
||||||
} else {
|
} else {
|
||||||
atom = MTMathAtomFactory.atom(for: String(char))
|
atom = MTMathAtomFactory.atom(forCharacter: char)
|
||||||
|
|
||||||
if atom == nil {
|
if atom == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(atom != nil, "Atom shouldn't be nil")
|
assert(atom != nil, "Atom shouldn't be nil")
|
||||||
|
atom?.fontStyle = currentFontStyle
|
||||||
if atom == nil {
|
|
||||||
print("wtf atom why nil?")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
list.add(atom!)
|
list.add(atom!)
|
||||||
prevAtom = atom
|
prevAtom = atom
|
||||||
|
|
||||||
@@ -462,12 +462,14 @@ public class MTMathListBuilder {
|
|||||||
|
|
||||||
if stop != nil {
|
if stop != nil {
|
||||||
if stop == "}" {
|
if stop == "}" {
|
||||||
print("Missing Closing Brace")
|
// We did not find a corresponding closing brace.
|
||||||
|
self.setError(.mismatchBraces, message:"Missing closing brace")
|
||||||
} else {
|
} else {
|
||||||
print("Expected Character not found: \(stop!)")
|
// we never found our stop character
|
||||||
|
let errorMessage = "Expected character not found: \(stop!)"
|
||||||
|
self.setError(.characterNotFound, message:errorMessage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return list
|
return list
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -475,7 +477,7 @@ public class MTMathListBuilder {
|
|||||||
if let atom = MTMathAtomFactory.atom(forLatexSymbol: command) {
|
if let atom = MTMathAtomFactory.atom(forLatexSymbol: command) {
|
||||||
return atom
|
return atom
|
||||||
}
|
}
|
||||||
if let accent = MTMathAtomFactory.getAccent(withName: command) {
|
if let accent = MTMathAtomFactory.accent(withName: command) {
|
||||||
// The command is an accent
|
// The command is an accent
|
||||||
accent.innerList = self.buildInternal(true)
|
accent.innerList = self.buildInternal(true)
|
||||||
return accent;
|
return accent;
|
||||||
@@ -499,7 +501,7 @@ public class MTMathListBuilder {
|
|||||||
let ch = self.getNextCharacter()
|
let ch = self.getNextCharacter()
|
||||||
if (ch == "[") {
|
if (ch == "[") {
|
||||||
// special handling for sqrt[degree]{radicand}
|
// special handling for sqrt[degree]{radicand}
|
||||||
rad.degree = self.buildInternal(false, stop:"]")
|
rad.degree = self.buildInternal(false, stopChar:"]")
|
||||||
rad.radicand = self.buildInternal(true)
|
rad.radicand = self.buildInternal(true)
|
||||||
} else {
|
} else {
|
||||||
self.unlookCharacter()
|
self.unlookCharacter()
|
||||||
@@ -574,8 +576,8 @@ public class MTMathListBuilder {
|
|||||||
// a string of all upper and lower case characters.
|
// a string of all upper and lower case characters.
|
||||||
var mutable = ""
|
var mutable = ""
|
||||||
while self.hasCharacters {
|
while self.hasCharacters {
|
||||||
let ch = self.getNextCharacter()!
|
let ch = self.getNextCharacter()
|
||||||
if (ch == "#" || (ch >= "A" && ch <= "F") || (ch >= "a" && ch <= "f") || (ch >= "0" && ch <= "9")) {
|
if ch == "#" || (ch >= "A" && ch <= "F") || (ch >= "a" && ch <= "f") || (ch >= "0" && ch <= "9") {
|
||||||
mutable.append(ch) // appendString:[NSString stringWithCharacters:&ch length:1]];
|
mutable.append(ch) // appendString:[NSString stringWithCharacters:&ch length:1]];
|
||||||
} else {
|
} else {
|
||||||
// we went too far
|
// we went too far
|
||||||
@@ -594,7 +596,7 @@ public class MTMathListBuilder {
|
|||||||
|
|
||||||
func skipSpaces() {
|
func skipSpaces() {
|
||||||
while self.hasCharacters {
|
while self.hasCharacters {
|
||||||
let ch = self.getNextCharacter()?.asciiValue ?? 0
|
let ch = self.getNextCharacter().utf32Char
|
||||||
if ch < 0x21 || ch > 0x7E {
|
if ch < 0x21 || ch > 0x7E {
|
||||||
// skip non ascii characters and spaces
|
// skip non ascii characters and spaces
|
||||||
continue;
|
continue;
|
||||||
@@ -605,7 +607,7 @@ public class MTMathListBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func stopCommand(_ command: String, list:MTMathList, stop:Character?) -> MTMathList? {
|
func stopCommand(_ command: String, list:MTMathList, stopChar:Character?) -> MTMathList? {
|
||||||
var fractionCommands: [String:[Character]] {
|
var fractionCommands: [String:[Character]] {
|
||||||
[
|
[
|
||||||
"over": [],
|
"over": [],
|
||||||
@@ -627,20 +629,19 @@ public class MTMathListBuilder {
|
|||||||
}
|
}
|
||||||
// return the list read so far.
|
// return the list read so far.
|
||||||
return list
|
return list
|
||||||
} else if let _ = fractionCommands[command] {
|
} else if let delims = fractionCommands[command] {
|
||||||
var frac:MTFraction! = nil;
|
var frac:MTFraction! = nil;
|
||||||
if command == "over" {
|
if command == "over" {
|
||||||
frac = MTFraction()
|
frac = MTFraction()
|
||||||
} else {
|
} else {
|
||||||
frac = MTFraction(hasRule: false)
|
frac = MTFraction(hasRule: false)
|
||||||
}
|
}
|
||||||
let delims = fractionCommands[command]!
|
|
||||||
if delims.count == 2 {
|
if delims.count == 2 {
|
||||||
frac.leftDelimiter = String(delims[0])
|
frac.leftDelimiter = String(delims[0])
|
||||||
frac.rightDelimiter = String(delims[1])
|
frac.rightDelimiter = String(delims[1])
|
||||||
}
|
}
|
||||||
frac.numerator = list;
|
frac.numerator = list;
|
||||||
frac.denominator = self.buildInternal(false, stop: stop)
|
frac.denominator = self.buildInternal(false, stopChar: stopChar)
|
||||||
if error != nil {
|
if error != nil {
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
@@ -658,7 +659,7 @@ public class MTMathListBuilder {
|
|||||||
return MTMathList(atom: table!)
|
return MTMathList(atom: table!)
|
||||||
}
|
}
|
||||||
} else if command == "end" {
|
} else if command == "end" {
|
||||||
if currentEnv != nil {
|
if currentEnv == nil {
|
||||||
let errorMessage = "Missing \\begin";
|
let errorMessage = "Missing \\begin";
|
||||||
self.setError(.missingBegin, message:errorMessage)
|
self.setError(.missingBegin, message:errorMessage)
|
||||||
return nil;
|
return nil;
|
||||||
@@ -691,7 +692,7 @@ public class MTMathListBuilder {
|
|||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
} else if modifier == "nolimits" {
|
} else if modifier == "nolimits" {
|
||||||
if atom!.type != .largeOperator {
|
if atom?.type != .largeOperator {
|
||||||
let errorMessage = "No limits can only be applied to an operator."
|
let errorMessage = "No limits can only be applied to an operator."
|
||||||
self.setError(.invalidLimits, message:errorMessage)
|
self.setError(.invalidLimits, message:errorMessage)
|
||||||
return true
|
return true
|
||||||
@@ -715,8 +716,7 @@ public class MTMathListBuilder {
|
|||||||
if let atom = MTMathAtomFactory.atom(forLatexSymbol: command) {
|
if let atom = MTMathAtomFactory.atom(forLatexSymbol: command) {
|
||||||
return atom
|
return atom
|
||||||
}
|
}
|
||||||
|
if let accent = MTMathAtomFactory.accent(withName: command) {
|
||||||
if let accent = MTMathAtomFactory.getAccent(withName: command) {
|
|
||||||
accent.innerList = self.buildInternal(true)
|
accent.innerList = self.buildInternal(true)
|
||||||
return accent
|
return accent
|
||||||
} else if command == "frac" {
|
} else if command == "frac" {
|
||||||
@@ -734,9 +734,8 @@ public class MTMathListBuilder {
|
|||||||
} else if command == "sqrt" {
|
} else if command == "sqrt" {
|
||||||
let rad = MTRadical()
|
let rad = MTRadical()
|
||||||
let char = self.getNextCharacter()
|
let char = self.getNextCharacter()
|
||||||
|
|
||||||
if char == "[" {
|
if char == "[" {
|
||||||
rad.degree = self.buildInternal(false, stop: "]")
|
rad.degree = self.buildInternal(false, stopChar: "]")
|
||||||
rad.radicand = self.buildInternal(true)
|
rad.radicand = self.buildInternal(true)
|
||||||
} else {
|
} else {
|
||||||
self.unlookCharacter()
|
self.unlookCharacter()
|
||||||
@@ -752,7 +751,7 @@ public class MTMathListBuilder {
|
|||||||
}
|
}
|
||||||
self.currentInnerAtom!.innerList = self.buildInternal(false)
|
self.currentInnerAtom!.innerList = self.buildInternal(false)
|
||||||
if self.currentInnerAtom?.rightBoundary == nil {
|
if self.currentInnerAtom?.rightBoundary == nil {
|
||||||
print("Missing \\right")
|
self.setError(.missingRight, message: "Missing \\right")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
let newInner = self.currentInnerAtom
|
let newInner = self.currentInnerAtom
|
||||||
@@ -775,86 +774,24 @@ public class MTMathListBuilder {
|
|||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
} else if command == "color" {
|
||||||
|
// A color command has 2 arguments
|
||||||
|
let mathColor = MTMathColor()
|
||||||
|
mathColor.colorString = self.readColor()!
|
||||||
|
mathColor.innerList = self.buildInternal(true)
|
||||||
|
return mathColor
|
||||||
|
} else if command == "colorbox" {
|
||||||
|
// A color command has 2 arguments
|
||||||
|
let mathColorbox = MTMathColorbox()
|
||||||
|
mathColorbox.colorString = self.readColor()!
|
||||||
|
mathColorbox.innerList = self.buildInternal(true)
|
||||||
|
return mathColorbox
|
||||||
} else {
|
} else {
|
||||||
print("Invalid Command")
|
self.setError(.invalidCommand, message: "Invalid command \\\(command)")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// func stop(command: String, list: MTMathList, stopChar: Character) -> MTMathList? {
|
|
||||||
// let fractionCommands = [
|
|
||||||
// "over": [],
|
|
||||||
// "atop": [],
|
|
||||||
// "choose": ["(", ")"],
|
|
||||||
// "brack": ["[", "]"],
|
|
||||||
// "brace": ["{", "}"]
|
|
||||||
// ]
|
|
||||||
//
|
|
||||||
// if command == "right" {
|
|
||||||
// if self.currentInnerAtom == nil {
|
|
||||||
// print("missing left")
|
|
||||||
// return nil
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if let rightBoundary = self.getBoundaryAtom("right") {
|
|
||||||
// self.currentInnerAtom!.rightBoundary = rightBoundary
|
|
||||||
//
|
|
||||||
// return list
|
|
||||||
// } else {
|
|
||||||
// return nil
|
|
||||||
// }
|
|
||||||
// } else if let delims = fractionCommands[command] {
|
|
||||||
// let frac: MTFraction
|
|
||||||
// if command == "over" {
|
|
||||||
// frac = MTFraction()
|
|
||||||
// } else {
|
|
||||||
// frac = MTFraction(hasRule: false)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if delims.count == 2 {
|
|
||||||
// frac.leftDelimiter = delims[0]
|
|
||||||
// frac.rightDelimiter = delims[1]
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// frac.numerator = list
|
|
||||||
// frac.denominator = self.buildInternal(false, stopChar: stopChar)
|
|
||||||
//
|
|
||||||
// let fracList = MTMathList()
|
|
||||||
// fracList.add(frac)
|
|
||||||
// return fracList
|
|
||||||
// } else if command == "\\" || command == "cr" {
|
|
||||||
// if currentEnv != nil {
|
|
||||||
// currentEnv!.numRows += 1
|
|
||||||
// return list
|
|
||||||
// } else {
|
|
||||||
// if let table = self.buildTable(env: nil, firstList: list, isRow: true) {
|
|
||||||
// return MTMathList(atoms: [table])
|
|
||||||
// } else {
|
|
||||||
// return nil
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// } else if command == "end" {
|
|
||||||
// if self.currentEnv == nil {
|
|
||||||
// print("Missing \\begin")
|
|
||||||
// return nil
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if let env = self.readEnvironment() {
|
|
||||||
// if env != self.currentEnv?.envName {
|
|
||||||
// print("Begin environment name \(currentEnv!.envName!) does not match end name: \(env)")
|
|
||||||
// return nil
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// currentEnv?.ended = true
|
|
||||||
//
|
|
||||||
// return list
|
|
||||||
// } else {
|
|
||||||
// return nil
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// return nil
|
|
||||||
// }
|
|
||||||
|
|
||||||
func readEnvironment() -> String? {
|
func readEnvironment() -> String? {
|
||||||
if !self.expectCharacter("{") {
|
if !self.expectCharacter("{") {
|
||||||
// We didn't find an opening brace, so no env found.
|
// We didn't find an opening brace, so no env found.
|
||||||
@@ -862,7 +799,7 @@ public class MTMathListBuilder {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
self.skipSpace()
|
self.skipSpaces()
|
||||||
let env = self.readString()
|
let env = self.readString()
|
||||||
|
|
||||||
if !self.expectCharacter("}") {
|
if !self.expectCharacter("}") {
|
||||||
@@ -873,13 +810,18 @@ public class MTMathListBuilder {
|
|||||||
return env
|
return env
|
||||||
}
|
}
|
||||||
|
|
||||||
func expectCharacter(_ char: Character) -> Bool {
|
func MTAssertNotSpace(_ ch: Character) {
|
||||||
self.skipSpace()
|
assert(ch >= "\u{21}" && ch <= "\u{7E}", "Expected non-space character \(ch)")
|
||||||
|
}
|
||||||
|
|
||||||
|
func expectCharacter(_ ch: Character) -> Bool {
|
||||||
|
MTAssertNotSpace(ch)
|
||||||
|
self.skipSpaces()
|
||||||
|
|
||||||
if self.hasCharacters {
|
if self.hasCharacters {
|
||||||
let nextChar = self.getNextCharacter()!
|
let nextChar = self.getNextCharacter()
|
||||||
|
MTAssertNotSpace(nextChar)
|
||||||
if nextChar == char {
|
if nextChar == ch {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
self.unlookCharacter()
|
self.unlookCharacter()
|
||||||
@@ -899,64 +841,68 @@ public class MTMathListBuilder {
|
|||||||
var currentCol = 0
|
var currentCol = 0
|
||||||
|
|
||||||
var rows = [[MTMathList]]()
|
var rows = [[MTMathList]]()
|
||||||
|
rows.append([MTMathList]())
|
||||||
if firstList != nil {
|
if firstList != nil {
|
||||||
rows[currentRow][currentCol] = firstList!
|
rows[currentRow].append(firstList!)
|
||||||
|
|
||||||
if isRow {
|
if isRow {
|
||||||
currentEnv?.numRows += 1
|
currentEnv!.numRows+=1
|
||||||
currentRow += 1
|
currentRow+=1
|
||||||
|
rows.append([MTMathList]())
|
||||||
} else {
|
} else {
|
||||||
currentCol += 1
|
currentCol+=1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
while !currentEnv!.ended && self.hasCharacters {
|
while !currentEnv!.ended && self.hasCharacters {
|
||||||
if let list = self.buildInternal(false) {
|
let list = self.buildInternal(false)
|
||||||
rows[currentRow][currentCol] = list
|
if list == nil {
|
||||||
currentCol += 1
|
// If there is an error building the list, bail out early.
|
||||||
if self.currentEnv!.numRows > currentRow {
|
return nil
|
||||||
currentRow = self.currentEnv!.numRows
|
}
|
||||||
|
rows[currentRow].append(list!)
|
||||||
|
currentCol+=1
|
||||||
|
if currentEnv!.numRows > currentRow {
|
||||||
|
currentRow = currentEnv!.numRows
|
||||||
|
rows.append([MTMathList]())
|
||||||
currentCol = 0
|
currentCol = 0
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !currentEnv!.ended && currentEnv?.envName == nil {
|
if !currentEnv!.ended && currentEnv!.envName != nil {
|
||||||
print("Missing \\end")
|
self.setError(.missingEnd, message: "Missing \\end")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if let table = MTMathAtomFactory.table(withEnvironment: currentEnv?.envName, rows: rows) {
|
var error:NSError?
|
||||||
|
let table = MTMathAtomFactory.table(withEnvironment: currentEnv?.envName, rows: rows, error: &error)
|
||||||
|
if table == nil && self.error == nil {
|
||||||
|
self.error = error
|
||||||
|
return nil
|
||||||
|
}
|
||||||
self.currentEnv = oldEnv
|
self.currentEnv = oldEnv
|
||||||
return table
|
return table
|
||||||
} else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getBoundaryAtom(_ delimiterType: String) -> MTMathAtom? {
|
func getBoundaryAtom(_ delimiterType: String) -> MTMathAtom? {
|
||||||
let delim = self.readDelimiter()
|
let delim = self.readDelimiter()
|
||||||
if delim == nil {
|
if delim == nil {
|
||||||
assertionFailure("Missing delimiter for \(delimiterType)")
|
let errorMessage = "Missing delimiter for \\\(delimiterType)"
|
||||||
|
self.setError(.missingDelimiter, message:errorMessage)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
let boundary = MTMathAtomFactory.boundary(forDelimiter: delim!)
|
let boundary = MTMathAtomFactory.boundary(forDelimiter: delim!)
|
||||||
if boundary == nil {
|
if boundary == nil {
|
||||||
assertionFailure("Invalid delimiter for \(delimiterType): \(delim!)")
|
let errorMessage = "Invalid delimiter for \(delimiterType): \(delim!)"
|
||||||
|
self.setError(.missingDelimiter, message:errorMessage)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return boundary
|
return boundary
|
||||||
}
|
}
|
||||||
|
|
||||||
func readDelimiter() -> String? {
|
func readDelimiter() -> String? {
|
||||||
self.skipSpace()
|
self.skipSpaces()
|
||||||
|
|
||||||
while self.hasCharacters {
|
while self.hasCharacters {
|
||||||
let char = self.getNextCharacter()!
|
let char = self.getNextCharacter()
|
||||||
|
MTAssertNotSpace(char)
|
||||||
if char == "\\" {
|
if char == "\\" {
|
||||||
let command = self.readCommand()
|
let command = self.readCommand()
|
||||||
if command == "|" {
|
if command == "|" {
|
||||||
@@ -970,31 +916,16 @@ public class MTMathListBuilder {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func skipSpace() {
|
|
||||||
while self.hasCharacters {
|
|
||||||
let char = self.getNextCharacter()
|
|
||||||
let asciiCharacterSet = CharacterSet(charactersIn: UnicodeScalar(0x21)...UnicodeScalar(0x7e))
|
|
||||||
if String(char!).rangeOfCharacter(from: asciiCharacterSet) != nil {
|
|
||||||
self.unlookCharacter()
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func readCommand() -> String {
|
func readCommand() -> String {
|
||||||
let singleChars = Array("{}$#%_| ,>;!\\")
|
let singleChars = "{}$#%_| ,>;!\\"
|
||||||
if self.hasCharacters {
|
if self.hasCharacters {
|
||||||
if let char = self.getNextCharacter() {
|
let char = self.getNextCharacter()
|
||||||
if let _ = singleChars.firstIndex(of: char) {
|
if let _ = singleChars.firstIndex(of: char) {
|
||||||
return String(char)
|
return String(char)
|
||||||
} else {
|
} else {
|
||||||
self.unlookCharacter()
|
self.unlookCharacter()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return self.readString()
|
return self.readString()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1002,7 +933,7 @@ public class MTMathListBuilder {
|
|||||||
// a string of all upper and lower case characters.
|
// a string of all upper and lower case characters.
|
||||||
var output = ""
|
var output = ""
|
||||||
while self.hasCharacters {
|
while self.hasCharacters {
|
||||||
if let char = self.getNextCharacter() {
|
let char = self.getNextCharacter()
|
||||||
if char.isLowercase || char.isUppercase {
|
if char.isLowercase || char.isUppercase {
|
||||||
output.append(char)
|
output.append(char)
|
||||||
} else {
|
} else {
|
||||||
@@ -1010,7 +941,6 @@ public class MTMathListBuilder {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return output
|
return output
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,12 +71,12 @@ class MTMathUILabel : MTView {
|
|||||||
var latex = "" {
|
var latex = "" {
|
||||||
didSet {
|
didSet {
|
||||||
self.error = nil
|
self.error = nil
|
||||||
var error: NSError? = nil
|
var error = NSError()
|
||||||
self.mathList = MTMathListBuilder.build(fromString: latex, error: &error)
|
self.mathList = MTMathListBuilder.build(fromString: latex, error: &error)
|
||||||
if error != nil {
|
if error != NSError() {
|
||||||
self.mathList = nil
|
self.mathList = nil
|
||||||
self.error = error
|
self.error = error
|
||||||
self.errorLabel?.text = error?.localizedDescription
|
self.errorLabel?.text = error.localizedDescription
|
||||||
self.errorLabel?.frame = self.bounds
|
self.errorLabel?.frame = self.bounds
|
||||||
self.errorLabel?.isHidden = !self.displayErrorInline
|
self.errorLabel?.isHidden = !self.displayErrorInline
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1637,7 +1637,7 @@ class MTTypesetter {
|
|||||||
for i in 0..<cols.count {
|
for i in 0..<cols.count {
|
||||||
let col = cols[i]
|
let col = cols[i]
|
||||||
let colWidth = columnWidths[i]
|
let colWidth = columnWidths[i]
|
||||||
let alignment = table?.getAlignmentOf(col: i)
|
let alignment = table?.get(alignmentForColumn: i)
|
||||||
var cellPos = columnStart;
|
var cellPos = columnStart;
|
||||||
switch alignment {
|
switch alignment {
|
||||||
case .right:
|
case .right:
|
||||||
|
|||||||
@@ -47,6 +47,34 @@ final class SwiftMathRenderTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getTestData() -> [TestRecord] {
|
||||||
|
[
|
||||||
|
TestRecord(build: "x", atomType: [.variable ], types: [], result: "x"),
|
||||||
|
TestRecord(build: "1", atomType: [.number ] , types: [], result: "1"),
|
||||||
|
TestRecord(build: "*", atomType: [.binaryOperator ] ,types: [], result:"*"),
|
||||||
|
TestRecord(build: "+", atomType: [.binaryOperator ], types: [], result:"+"),
|
||||||
|
TestRecord(build: ".", atomType: [.number ], types: [], result:"."),
|
||||||
|
TestRecord(build: "(", atomType: [.open ], types: [], result:"(" ),
|
||||||
|
TestRecord(build: ")", atomType: [.close ], types: [], result:")"),
|
||||||
|
TestRecord(build: ",", atomType: [.punctuation], types: [], result:","),
|
||||||
|
TestRecord(build: "!", atomType: [.close], types: [], result:"!"),
|
||||||
|
TestRecord(build: "=", atomType: [.relation], types: [], result:"="),
|
||||||
|
TestRecord(build: "x+2", atomType: [.variable, .binaryOperator, .number ], types: [], result:"x+2"),
|
||||||
|
// spaces are ignored
|
||||||
|
TestRecord(build: "(2.3 * 8)", atomType: [.open, .number, .number, .number, .binaryOperator, .number , .close ], types: [], result:"(2.3*8)"),
|
||||||
|
// braces are just for grouping
|
||||||
|
TestRecord(build: "5{3+4}", atomType: [.number, .number, .binaryOperator, .number], types: [], result:"53+4"),
|
||||||
|
// commands
|
||||||
|
TestRecord(build: "\\pi+\\theta\\geq 3",atomType: [.variable, .binaryOperator, .variable, .relation, .number], types: [], result:"\\pi +\\theta \\geq 3"),
|
||||||
|
// aliases
|
||||||
|
TestRecord(build: "\\pi\\ne 5 \\land 3", atomType: [.variable, .relation, .number, .binaryOperator, .number], types: [], result:"\\pi \\neq 5\\wedge 3"),
|
||||||
|
// control space
|
||||||
|
TestRecord(build: "x \\ y", atomType: [ .variable, .ordinary, .variable], types: [], result:"x\\ y"),
|
||||||
|
// spacing
|
||||||
|
TestRecord(build: "x \\quad y \\; z \\! q", atomType: [ .variable, .space, .variable,.space, .variable, .space, .variable], types: [], result:"x\\quad y\\; z\\! q")
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
func getTestDataSuperScript() -> [TestRecord] {
|
func getTestDataSuperScript() -> [TestRecord] {
|
||||||
[
|
[
|
||||||
TestRecord(build: "x^2", atomType: [.variable], types: [.number], result: "x^{2}"),
|
TestRecord(build: "x^2", atomType: [.variable], types: [.number], result: "x^{2}"),
|
||||||
@@ -125,13 +153,68 @@ final class SwiftMathRenderTests: XCTestCase {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getTestDataParseErrors() -> [(String, MTParseErrors)] {
|
||||||
|
return [
|
||||||
|
("}a", .mismatchBraces),
|
||||||
|
("\\notacommand", .invalidCommand),
|
||||||
|
("\\sqrt[5+3", .characterNotFound),
|
||||||
|
("{5+3", .mismatchBraces),
|
||||||
|
("5+3}", .mismatchBraces),
|
||||||
|
("{1+\\frac{3+2", .mismatchBraces),
|
||||||
|
("1+\\left", .missingDelimiter),
|
||||||
|
("\\left(\\frac12\\right", .missingDelimiter),
|
||||||
|
("\\left 5 + 3 \\right)", .invalidDelimiter),
|
||||||
|
("\\left(\\frac12\\right + 3", .invalidDelimiter),
|
||||||
|
("\\left\\lmoustache 5 + 3 \\right)", .invalidDelimiter),
|
||||||
|
("\\left(\\frac12\\right\\rmoustache + 3", .invalidDelimiter),
|
||||||
|
("5 + 3 \\right)", .missingLeft),
|
||||||
|
("\\left(\\frac12", .missingRight),
|
||||||
|
("\\left(5 + \\left| \\frac12 \\right)", .missingRight),
|
||||||
|
("5+ \\left|\\frac12\\right| \\right)", .missingLeft),
|
||||||
|
("\\begin matrix \\end matrix", .characterNotFound), // missing {
|
||||||
|
("\\begin", .characterNotFound), // missing {
|
||||||
|
("\\begin{", .characterNotFound), // missing }
|
||||||
|
("\\begin{matrix parens}", .characterNotFound), // missing } (no spaces in env)
|
||||||
|
("\\begin{matrix} x", .missingEnd),
|
||||||
|
("\\begin{matrix} x \\end", .characterNotFound), // missing {
|
||||||
|
("\\begin{matrix} x \\end + 3", .characterNotFound), // missing {
|
||||||
|
("\\begin{matrix} x \\end{", .characterNotFound), // missing }
|
||||||
|
("\\begin{matrix} x \\end{matrix + 3", .characterNotFound), // missing }
|
||||||
|
("\\begin{matrix} x \\end{pmatrix}", .invalidEnv),
|
||||||
|
("x \\end{matrix}", .missingBegin),
|
||||||
|
("\\begin{notanenv} x \\end{notanenv}", .invalidEnv),
|
||||||
|
("\\begin{matrix} \\notacommand \\end{matrix}", .invalidCommand),
|
||||||
|
("\\begin{displaylines} x & y \\end{displaylines}", .invalidNumColumns),
|
||||||
|
("\\begin{eqalign} x \\end{eqalign}", .invalidNumColumns),
|
||||||
|
("\\nolimits", .invalidLimits),
|
||||||
|
("\\frac\\limits{1}{2}", .invalidLimits)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
func testBuilder() throws {
|
||||||
|
let data = getTestData()
|
||||||
|
for testCase in data {
|
||||||
|
let str = testCase.build
|
||||||
|
var error = NSError()
|
||||||
|
let list = MTMathListBuilder.build(fromString: str, error: &error)
|
||||||
|
XCTAssert(error.code == 0)
|
||||||
|
let desc = "Error for string:\(str)"
|
||||||
|
let atomTypes = testCase.atomType
|
||||||
|
self.checkAtomTypes(list, types:atomTypes, desc:desc)
|
||||||
|
|
||||||
|
// convert it back to latex
|
||||||
|
let latex = MTMathListBuilder.mathListToString(list)
|
||||||
|
XCTAssertEqual(latex, testCase.result, desc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func testSuperScript() throws {
|
func testSuperScript() throws {
|
||||||
let data = getTestDataSuperScript()
|
let data = getTestDataSuperScript()
|
||||||
for testCase in data {
|
for testCase in data {
|
||||||
let str = testCase.build
|
let str = testCase.build
|
||||||
var error:NSError?
|
var error = NSError()
|
||||||
let list = MTMathListBuilder.build(fromString: str, error:&error)
|
let list = MTMathListBuilder.build(fromString: str, error:&error)
|
||||||
XCTAssertNil(error)
|
XCTAssert(error.code == NSNotFound)
|
||||||
let desc = "Error for string:\(str)"
|
let desc = "Error for string:\(str)"
|
||||||
let atomTypes = testCase.atomType
|
let atomTypes = testCase.atomType
|
||||||
checkAtomTypes(list, types:atomTypes, desc:desc)
|
checkAtomTypes(list, types:atomTypes, desc:desc)
|
||||||
@@ -163,9 +246,9 @@ final class SwiftMathRenderTests: XCTestCase {
|
|||||||
let data = getTestDataSubScript()
|
let data = getTestDataSubScript()
|
||||||
for testCase in data {
|
for testCase in data {
|
||||||
let str = testCase.build
|
let str = testCase.build
|
||||||
var error:NSError?
|
var error = NSError()
|
||||||
let list = MTMathListBuilder.build(fromString: str, error:&error)
|
let list = MTMathListBuilder.build(fromString: str, error:&error)
|
||||||
XCTAssertNil(error)
|
XCTAssert(error.code == NSNotFound)
|
||||||
let desc = "Error for string:\(str)"
|
let desc = "Error for string:\(str)"
|
||||||
let atomTypes = testCase.atomType
|
let atomTypes = testCase.atomType
|
||||||
checkAtomTypes(list, types:atomTypes, desc:desc)
|
checkAtomTypes(list, types:atomTypes, desc:desc)
|
||||||
@@ -197,9 +280,9 @@ final class SwiftMathRenderTests: XCTestCase {
|
|||||||
let data = getTestDataSuperSubScript()
|
let data = getTestDataSuperSubScript()
|
||||||
for testCase in data {
|
for testCase in data {
|
||||||
let str = testCase.build
|
let str = testCase.build
|
||||||
var error:NSError?
|
var error = NSError()
|
||||||
let list = MTMathListBuilder.build(fromString: str, error:&error)
|
let list = MTMathListBuilder.build(fromString: str, error:&error)
|
||||||
XCTAssertNil(error)
|
XCTAssert(error.code == NSNotFound)
|
||||||
let desc = "Error for string:\(str)"
|
let desc = "Error for string:\(str)"
|
||||||
let atomTypes = testCase.atomType
|
let atomTypes = testCase.atomType
|
||||||
checkAtomTypes(list, types:atomTypes, desc:desc)
|
checkAtomTypes(list, types:atomTypes, desc:desc)
|
||||||
@@ -425,7 +508,7 @@ final class SwiftMathRenderTests: XCTestCase {
|
|||||||
for testCase in data {
|
for testCase in data {
|
||||||
let str = testCase.build
|
let str = testCase.build
|
||||||
|
|
||||||
var error:NSError?
|
var error = NSError()
|
||||||
let list = MTMathListBuilder.build(fromString: str, error: &error)!
|
let list = MTMathListBuilder.build(fromString: str, error: &error)!
|
||||||
|
|
||||||
XCTAssertNotNil(list, str);
|
XCTAssertNotNil(list, str);
|
||||||
@@ -456,6 +539,824 @@ final class SwiftMathRenderTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testOver() throws {
|
||||||
|
let str = "1 \\over c";
|
||||||
|
let list = MTMathListBuilder.build(fromString:str)!
|
||||||
|
let desc = "Error for string:\(str)"
|
||||||
|
|
||||||
|
XCTAssertNotNil(list, desc);
|
||||||
|
XCTAssertEqual((list.atoms.count), 1, desc);
|
||||||
|
let frac = list.atoms[0] as! MTFraction
|
||||||
|
XCTAssertEqual(frac.type, .fraction, desc);
|
||||||
|
XCTAssertEqual(frac.nucleus, "", desc);
|
||||||
|
XCTAssertTrue(frac.hasRule);
|
||||||
|
XCTAssertNil(frac.rightDelimiter);
|
||||||
|
XCTAssertNil(frac.leftDelimiter);
|
||||||
|
|
||||||
|
var subList = frac.numerator!
|
||||||
|
XCTAssertNotNil(subList, desc);
|
||||||
|
XCTAssertEqual((subList.atoms.count), 1, desc);
|
||||||
|
var atom = subList.atoms[0];
|
||||||
|
XCTAssertEqual(atom.type, .number, desc);
|
||||||
|
XCTAssertEqual(atom.nucleus, "1", desc);
|
||||||
|
|
||||||
|
atom = list.atoms[0];
|
||||||
|
subList = frac.denominator!
|
||||||
|
XCTAssertNotNil(subList, desc);
|
||||||
|
XCTAssertEqual((subList.atoms.count), 1, desc);
|
||||||
|
atom = subList.atoms[0];
|
||||||
|
XCTAssertEqual(atom.type, .variable, desc);
|
||||||
|
XCTAssertEqual(atom.nucleus, "c", desc);
|
||||||
|
|
||||||
|
// convert it back to latex
|
||||||
|
let latex = MTMathListBuilder.mathListToString(list)
|
||||||
|
XCTAssertEqual(latex, "\\frac{1}{c}", desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
func testOverInParens() throws {
|
||||||
|
let str = "5 + {1 \\over c} + 8";
|
||||||
|
let list = MTMathListBuilder.build(fromString:str)!
|
||||||
|
let desc = "Error for string:\(str)"
|
||||||
|
|
||||||
|
XCTAssertNotNil(list, desc);
|
||||||
|
XCTAssertEqual((list.atoms.count), 5, desc);
|
||||||
|
let types = [MTMathAtomType.number, .binaryOperator, .fraction, .binaryOperator, .number]
|
||||||
|
self.checkAtomTypes(list, types:types, desc:desc)
|
||||||
|
|
||||||
|
let frac = list.atoms[2] as! MTFraction
|
||||||
|
XCTAssertEqual(frac.type, .fraction, desc);
|
||||||
|
XCTAssertEqual(frac.nucleus, "", desc);
|
||||||
|
XCTAssertTrue(frac.hasRule);
|
||||||
|
XCTAssertNil(frac.rightDelimiter);
|
||||||
|
XCTAssertNil(frac.leftDelimiter);
|
||||||
|
|
||||||
|
var subList = frac.numerator!
|
||||||
|
XCTAssertNotNil(subList, desc);
|
||||||
|
XCTAssertEqual((subList.atoms.count), 1, desc);
|
||||||
|
var atom = subList.atoms[0];
|
||||||
|
XCTAssertEqual(atom.type, .number, desc);
|
||||||
|
XCTAssertEqual(atom.nucleus, "1", desc);
|
||||||
|
|
||||||
|
atom = list.atoms[0];
|
||||||
|
subList = frac.denominator!
|
||||||
|
XCTAssertNotNil(subList, desc);
|
||||||
|
XCTAssertEqual((subList.atoms.count), 1, desc);
|
||||||
|
atom = subList.atoms[0];
|
||||||
|
XCTAssertEqual(atom.type, .variable, desc);
|
||||||
|
XCTAssertEqual(atom.nucleus, "c", desc);
|
||||||
|
|
||||||
|
// convert it back to latex
|
||||||
|
let latex = MTMathListBuilder.mathListToString(list)
|
||||||
|
XCTAssertEqual(latex, "5+\\frac{1}{c}+8", desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAtop() throws {
|
||||||
|
let str = "1 \\atop c";
|
||||||
|
let list = MTMathListBuilder.build(fromString:str)!
|
||||||
|
let desc = "Error for string:\(str)"
|
||||||
|
|
||||||
|
XCTAssertNotNil(list, desc);
|
||||||
|
XCTAssertEqual((list.atoms.count), 1, desc);
|
||||||
|
let frac = list.atoms[0] as! MTFraction
|
||||||
|
XCTAssertEqual(frac.type, .fraction, desc);
|
||||||
|
XCTAssertEqual(frac.nucleus, "", desc);
|
||||||
|
XCTAssertFalse(frac.hasRule);
|
||||||
|
XCTAssertNil(frac.rightDelimiter);
|
||||||
|
XCTAssertNil(frac.leftDelimiter);
|
||||||
|
|
||||||
|
var subList = frac.numerator!
|
||||||
|
XCTAssertNotNil(subList, desc);
|
||||||
|
XCTAssertEqual((subList.atoms.count), 1, desc);
|
||||||
|
var atom = subList.atoms[0];
|
||||||
|
XCTAssertEqual(atom.type, .number, desc);
|
||||||
|
XCTAssertEqual(atom.nucleus, "1", desc);
|
||||||
|
|
||||||
|
atom = list.atoms[0];
|
||||||
|
subList = frac.denominator!
|
||||||
|
XCTAssertNotNil(subList, desc);
|
||||||
|
XCTAssertEqual((subList.atoms.count), 1, desc);
|
||||||
|
atom = subList.atoms[0];
|
||||||
|
XCTAssertEqual(atom.type, .variable, desc);
|
||||||
|
XCTAssertEqual(atom.nucleus, "c", desc);
|
||||||
|
|
||||||
|
// convert it back to latex
|
||||||
|
let latex = MTMathListBuilder.mathListToString(list)
|
||||||
|
XCTAssertEqual(latex, "{1 \\atop c}", desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAtopInParens() throws {
|
||||||
|
let str = "5 + {1 \\atop c} + 8";
|
||||||
|
let list = MTMathListBuilder.build(fromString:str)!
|
||||||
|
let desc = "Error for string:\(str)"
|
||||||
|
|
||||||
|
XCTAssertNotNil(list, desc);
|
||||||
|
XCTAssertEqual((list.atoms.count), 5, desc);
|
||||||
|
let types = [MTMathAtomType.number, .binaryOperator, .fraction, .binaryOperator, .number]
|
||||||
|
self.checkAtomTypes(list, types:types, desc:desc)
|
||||||
|
|
||||||
|
let frac = list.atoms[2] as! MTFraction
|
||||||
|
XCTAssertEqual(frac.type, .fraction, desc);
|
||||||
|
XCTAssertEqual(frac.nucleus, "", desc);
|
||||||
|
XCTAssertFalse(frac.hasRule);
|
||||||
|
XCTAssertNil(frac.rightDelimiter);
|
||||||
|
XCTAssertNil(frac.leftDelimiter);
|
||||||
|
|
||||||
|
var subList = frac.numerator!
|
||||||
|
XCTAssertNotNil(subList, desc);
|
||||||
|
XCTAssertEqual((subList.atoms.count), 1, desc);
|
||||||
|
var atom = subList.atoms[0];
|
||||||
|
XCTAssertEqual(atom.type, .number, desc);
|
||||||
|
XCTAssertEqual(atom.nucleus, "1", desc);
|
||||||
|
|
||||||
|
atom = list.atoms[0];
|
||||||
|
subList = frac.denominator!
|
||||||
|
XCTAssertNotNil(subList, desc);
|
||||||
|
XCTAssertEqual((subList.atoms.count), 1, desc);
|
||||||
|
atom = subList.atoms[0];
|
||||||
|
XCTAssertEqual(atom.type, .variable, desc);
|
||||||
|
XCTAssertEqual(atom.nucleus, "c", desc);
|
||||||
|
|
||||||
|
// convert it back to latex
|
||||||
|
let latex = MTMathListBuilder.mathListToString(list)
|
||||||
|
XCTAssertEqual(latex, "5+{1 \\atop c}+8", desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
func testChoose() throws {
|
||||||
|
let str = "n \\choose k";
|
||||||
|
let list = MTMathListBuilder.build(fromString:str)!
|
||||||
|
let desc = "Error for string:\(str)"
|
||||||
|
|
||||||
|
XCTAssertNotNil(list, desc);
|
||||||
|
XCTAssertEqual((list.atoms.count), 1, desc);
|
||||||
|
let frac = list.atoms[0] as! MTFraction
|
||||||
|
XCTAssertEqual(frac.type, .fraction, desc);
|
||||||
|
XCTAssertEqual(frac.nucleus, "", desc);
|
||||||
|
XCTAssertFalse(frac.hasRule);
|
||||||
|
XCTAssertEqual(frac.rightDelimiter, ")");
|
||||||
|
XCTAssertEqual(frac.leftDelimiter, "(");
|
||||||
|
|
||||||
|
var subList = frac.numerator!
|
||||||
|
XCTAssertNotNil(subList, desc);
|
||||||
|
XCTAssertEqual((subList.atoms.count), 1, desc);
|
||||||
|
var atom = subList.atoms[0];
|
||||||
|
XCTAssertEqual(atom.type, .variable, desc);
|
||||||
|
XCTAssertEqual(atom.nucleus, "n", desc);
|
||||||
|
|
||||||
|
atom = list.atoms[0];
|
||||||
|
subList = frac.denominator!
|
||||||
|
XCTAssertNotNil(subList, desc);
|
||||||
|
XCTAssertEqual((subList.atoms.count), 1, desc);
|
||||||
|
atom = subList.atoms[0];
|
||||||
|
XCTAssertEqual(atom.type, .variable, desc);
|
||||||
|
XCTAssertEqual(atom.nucleus, "k", desc);
|
||||||
|
|
||||||
|
// convert it back to latex
|
||||||
|
let latex = MTMathListBuilder.mathListToString(list)
|
||||||
|
XCTAssertEqual(latex, "{n \\choose k}", desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
func testBrack() throws {
|
||||||
|
let str = "n \\brack k";
|
||||||
|
let list = MTMathListBuilder.build(fromString:str)!
|
||||||
|
let desc = "Error for string:\(str)"
|
||||||
|
|
||||||
|
XCTAssertNotNil(list, desc);
|
||||||
|
XCTAssertEqual((list.atoms.count), 1, desc);
|
||||||
|
let frac = list.atoms[0] as! MTFraction
|
||||||
|
XCTAssertEqual(frac.type, .fraction, desc);
|
||||||
|
XCTAssertEqual(frac.nucleus, "", desc);
|
||||||
|
XCTAssertFalse(frac.hasRule);
|
||||||
|
XCTAssertEqual(frac.rightDelimiter, "]");
|
||||||
|
XCTAssertEqual(frac.leftDelimiter, "[");
|
||||||
|
|
||||||
|
var subList = frac.numerator!
|
||||||
|
XCTAssertNotNil(subList, desc);
|
||||||
|
XCTAssertEqual((subList.atoms.count), 1, desc);
|
||||||
|
var atom = subList.atoms[0];
|
||||||
|
XCTAssertEqual(atom.type, .variable, desc);
|
||||||
|
XCTAssertEqual(atom.nucleus, "n", desc);
|
||||||
|
|
||||||
|
atom = list.atoms[0];
|
||||||
|
subList = frac.denominator!
|
||||||
|
XCTAssertNotNil(subList, desc);
|
||||||
|
XCTAssertEqual((subList.atoms.count), 1, desc);
|
||||||
|
atom = subList.atoms[0];
|
||||||
|
XCTAssertEqual(atom.type, .variable, desc);
|
||||||
|
XCTAssertEqual(atom.nucleus, "k", desc);
|
||||||
|
|
||||||
|
// convert it back to latex
|
||||||
|
let latex = MTMathListBuilder.mathListToString(list)
|
||||||
|
XCTAssertEqual(latex, "{n \\brack k}", desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
func testBrace() throws {
|
||||||
|
let str = "n \\brace k";
|
||||||
|
let list = MTMathListBuilder.build(fromString:str)!
|
||||||
|
let desc = "Error for string:\(str)"
|
||||||
|
|
||||||
|
XCTAssertNotNil(list, desc);
|
||||||
|
XCTAssertEqual((list.atoms.count), 1, desc);
|
||||||
|
let frac = list.atoms[0] as! MTFraction
|
||||||
|
XCTAssertEqual(frac.type, .fraction, desc);
|
||||||
|
XCTAssertEqual(frac.nucleus, "", desc);
|
||||||
|
XCTAssertFalse(frac.hasRule);
|
||||||
|
XCTAssertEqual(frac.rightDelimiter, "}");
|
||||||
|
XCTAssertEqual(frac.leftDelimiter, "{");
|
||||||
|
|
||||||
|
var subList = frac.numerator!
|
||||||
|
XCTAssertNotNil(subList, desc);
|
||||||
|
XCTAssertEqual((subList.atoms.count), 1, desc);
|
||||||
|
var atom = subList.atoms[0];
|
||||||
|
XCTAssertEqual(atom.type, .variable, desc);
|
||||||
|
XCTAssertEqual(atom.nucleus, "n", desc);
|
||||||
|
|
||||||
|
atom = list.atoms[0];
|
||||||
|
subList = frac.denominator!
|
||||||
|
XCTAssertNotNil(subList, desc);
|
||||||
|
XCTAssertEqual((subList.atoms.count), 1, desc);
|
||||||
|
atom = subList.atoms[0];
|
||||||
|
XCTAssertEqual(atom.type, .variable, desc);
|
||||||
|
XCTAssertEqual(atom.nucleus, "k", desc);
|
||||||
|
|
||||||
|
// convert it back to latex
|
||||||
|
let latex = MTMathListBuilder.mathListToString(list)
|
||||||
|
XCTAssertEqual(latex, "{n \\brace k}", desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
func testBinom() throws {
|
||||||
|
let str = "\\binom{n}{k}";
|
||||||
|
let list = MTMathListBuilder.build(fromString:str)!
|
||||||
|
let desc = "Error for string:\(str)"
|
||||||
|
|
||||||
|
XCTAssertNotNil(list, desc);
|
||||||
|
XCTAssertEqual((list.atoms.count), 1, desc);
|
||||||
|
let frac = list.atoms[0] as! MTFraction
|
||||||
|
XCTAssertEqual(frac.type, .fraction, desc);
|
||||||
|
XCTAssertEqual(frac.nucleus, "", desc);
|
||||||
|
XCTAssertFalse(frac.hasRule);
|
||||||
|
XCTAssertEqual(frac.rightDelimiter, ")");
|
||||||
|
XCTAssertEqual(frac.leftDelimiter, "(");
|
||||||
|
|
||||||
|
var subList = frac.numerator!
|
||||||
|
XCTAssertNotNil(subList, desc);
|
||||||
|
XCTAssertEqual((subList.atoms.count), 1, desc);
|
||||||
|
var atom = subList.atoms[0];
|
||||||
|
XCTAssertEqual(atom.type, .variable, desc);
|
||||||
|
XCTAssertEqual(atom.nucleus, "n", desc);
|
||||||
|
|
||||||
|
atom = list.atoms[0];
|
||||||
|
subList = frac.denominator!
|
||||||
|
XCTAssertNotNil(subList, desc);
|
||||||
|
XCTAssertEqual((subList.atoms.count), 1, desc);
|
||||||
|
atom = subList.atoms[0];
|
||||||
|
XCTAssertEqual(atom.type, .variable, desc);
|
||||||
|
XCTAssertEqual(atom.nucleus, "k", desc);
|
||||||
|
|
||||||
|
// convert it back to latex (binom converts to choose)
|
||||||
|
let latex = MTMathListBuilder.mathListToString(list)
|
||||||
|
XCTAssertEqual(latex, "{n \\choose k}", desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
func testOverLine() throws {
|
||||||
|
let str = "\\overline 2";
|
||||||
|
let list = MTMathListBuilder.build(fromString:str)!
|
||||||
|
let desc = "Error for string:\(str)"
|
||||||
|
|
||||||
|
XCTAssertNotNil(list, desc);
|
||||||
|
XCTAssertEqual((list.atoms.count), 1, desc);
|
||||||
|
let over = list.atoms[0] as! MTOverLine
|
||||||
|
XCTAssertEqual(over.type, .overline, desc);
|
||||||
|
XCTAssertEqual(over.nucleus, "", desc);
|
||||||
|
|
||||||
|
let subList = over.innerList!
|
||||||
|
XCTAssertNotNil(subList, desc);
|
||||||
|
XCTAssertEqual((subList.atoms.count), 1, desc);
|
||||||
|
let atom = subList.atoms[0];
|
||||||
|
XCTAssertEqual(atom.type, .number, desc);
|
||||||
|
XCTAssertEqual(atom.nucleus, "2", desc);
|
||||||
|
|
||||||
|
// convert it back to latex
|
||||||
|
let latex = MTMathListBuilder.mathListToString(list)
|
||||||
|
XCTAssertEqual(latex, "\\overline{2}", desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
func testUnderline() throws {
|
||||||
|
let str = "\\underline 2";
|
||||||
|
let list = MTMathListBuilder.build(fromString:str)!
|
||||||
|
let desc = "Error for string:\(str)"
|
||||||
|
|
||||||
|
XCTAssertNotNil(list, desc);
|
||||||
|
XCTAssertEqual((list.atoms.count), 1, desc);
|
||||||
|
let under = list.atoms[0] as! MTUnderLine
|
||||||
|
XCTAssertEqual(under.type, .underline, desc);
|
||||||
|
XCTAssertEqual(under.nucleus, "", desc);
|
||||||
|
|
||||||
|
let subList = under.innerList!
|
||||||
|
XCTAssertNotNil(subList, desc);
|
||||||
|
XCTAssertEqual((subList.atoms.count), 1, desc);
|
||||||
|
let atom = subList.atoms[0];
|
||||||
|
XCTAssertEqual(atom.type, .number, desc);
|
||||||
|
XCTAssertEqual(atom.nucleus, "2", desc);
|
||||||
|
|
||||||
|
// convert it back to latex
|
||||||
|
let latex = MTMathListBuilder.mathListToString(list)
|
||||||
|
XCTAssertEqual(latex, "\\underline{2}", desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccent() throws {
|
||||||
|
let str = "\\bar x";
|
||||||
|
let list = MTMathListBuilder.build(fromString:str)!
|
||||||
|
let desc = "Error for string:\(str)"
|
||||||
|
|
||||||
|
XCTAssertNotNil(list, desc);
|
||||||
|
XCTAssertEqual((list.atoms.count), 1, desc);
|
||||||
|
let accent = list.atoms[0] as! MTAccent
|
||||||
|
XCTAssertEqual(accent.type, .accent, desc);
|
||||||
|
XCTAssertEqual(accent.nucleus, "\u{0304}", desc);
|
||||||
|
|
||||||
|
let subList = accent.innerList!
|
||||||
|
XCTAssertNotNil(subList, desc);
|
||||||
|
XCTAssertEqual((subList.atoms.count), 1, desc);
|
||||||
|
let atom = subList.atoms[0];
|
||||||
|
XCTAssertEqual(atom.type, .variable, desc);
|
||||||
|
XCTAssertEqual(atom.nucleus, "x", desc);
|
||||||
|
|
||||||
|
// convert it back to latex
|
||||||
|
let latex = MTMathListBuilder.mathListToString(list)
|
||||||
|
XCTAssertEqual(latex, "\\bar{x}", desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
func testMathSpace() throws {
|
||||||
|
let str = "\\!";
|
||||||
|
let list = MTMathListBuilder.build(fromString:str)!
|
||||||
|
let desc = "Error for string:\(str)"
|
||||||
|
|
||||||
|
XCTAssertNotNil(list, desc);
|
||||||
|
XCTAssertEqual((list.atoms.count), 1, desc);
|
||||||
|
let space = list.atoms[0] as! MTMathSpace
|
||||||
|
XCTAssertEqual(space.type, .space, desc);
|
||||||
|
XCTAssertEqual(space.nucleus, "", desc);
|
||||||
|
XCTAssertEqual(space.space, -3);
|
||||||
|
|
||||||
|
// convert it back to latex
|
||||||
|
let latex = MTMathListBuilder.mathListToString(list)
|
||||||
|
XCTAssertEqual(latex, "\\! ", desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
func testMathStyle() throws {
|
||||||
|
let str = "\\textstyle y \\scriptstyle x";
|
||||||
|
let list = MTMathListBuilder.build(fromString:str)!
|
||||||
|
let desc = "Error for string:\(str)"
|
||||||
|
|
||||||
|
XCTAssertNotNil(list, desc);
|
||||||
|
XCTAssertEqual((list.atoms.count), 4, desc);
|
||||||
|
let style = list.atoms[0] as! MTMathStyle
|
||||||
|
XCTAssertEqual(style.type, .style, desc);
|
||||||
|
XCTAssertEqual(style.nucleus, "", desc);
|
||||||
|
XCTAssertEqual(style.style, .text);
|
||||||
|
|
||||||
|
let style2 = list.atoms[2] as! MTMathStyle
|
||||||
|
XCTAssertEqual(style2.type, .style, desc);
|
||||||
|
XCTAssertEqual(style2.nucleus, "", desc);
|
||||||
|
XCTAssertEqual(style2.style, .script);
|
||||||
|
|
||||||
|
// convert it back to latex
|
||||||
|
let latex = MTMathListBuilder.mathListToString(list)
|
||||||
|
XCTAssertEqual(latex, "\\textstyle y\\scriptstyle x", desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
func testMatrix() throws {
|
||||||
|
let str = "\\begin{matrix} x & y \\\\ z & w \\end{matrix}";
|
||||||
|
let list = MTMathListBuilder.build(fromString:str)!
|
||||||
|
|
||||||
|
XCTAssertNotNil(list);
|
||||||
|
XCTAssertEqual((list.atoms.count), 1);
|
||||||
|
let table = list.atoms[0] as! MTMathTable
|
||||||
|
XCTAssertEqual(table.type, .table);
|
||||||
|
XCTAssertEqual(table.nucleus, "");
|
||||||
|
XCTAssertEqual(table.environment, "matrix");
|
||||||
|
XCTAssertEqual(table.interRowAdditionalSpacing, 0);
|
||||||
|
XCTAssertEqual(table.interColumnSpacing, 18);
|
||||||
|
XCTAssertEqual(table.numRows, 2);
|
||||||
|
XCTAssertEqual(table.numColumns, 2);
|
||||||
|
|
||||||
|
for i in 0..<2 {
|
||||||
|
let alignment = table.get(alignmentForColumn:i)
|
||||||
|
XCTAssertEqual(alignment, .center);
|
||||||
|
for j in 0..<2 {
|
||||||
|
let cell = table.cells[j][i];
|
||||||
|
XCTAssertEqual(cell.atoms.count, 2);
|
||||||
|
let style = cell.atoms[0] as! MTMathStyle
|
||||||
|
XCTAssertEqual(style.type, .style);
|
||||||
|
XCTAssertEqual(style.style, .text);
|
||||||
|
|
||||||
|
let atom = cell.atoms[1];
|
||||||
|
XCTAssertEqual(atom.type, .variable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert it back to latex
|
||||||
|
let latex = MTMathListBuilder.mathListToString(list)
|
||||||
|
XCTAssertEqual(latex, "\\begin{matrix}x&y\\\\ z&w\\end{matrix}");
|
||||||
|
}
|
||||||
|
|
||||||
|
func testPMatrix() throws {
|
||||||
|
let str = "\\begin{pmatrix} x & y \\\\ z & w \\end{pmatrix}";
|
||||||
|
let list = MTMathListBuilder.build(fromString:str)!
|
||||||
|
|
||||||
|
XCTAssertNotNil(list);
|
||||||
|
XCTAssertEqual((list.atoms.count), 1);
|
||||||
|
let inner = list.atoms[0] as! MTInner
|
||||||
|
XCTAssertEqual(inner.type, .inner, str);
|
||||||
|
XCTAssertEqual(inner.nucleus, "", str);
|
||||||
|
|
||||||
|
let innerList = inner.innerList!
|
||||||
|
XCTAssertNotNil(innerList, str);
|
||||||
|
|
||||||
|
XCTAssertNotNil(inner.leftBoundary, str);
|
||||||
|
XCTAssertEqual(inner.leftBoundary!.type, .boundary, str);
|
||||||
|
XCTAssertEqual(inner.leftBoundary!.nucleus, "(", str);
|
||||||
|
|
||||||
|
XCTAssertNotNil(inner.rightBoundary, str);
|
||||||
|
XCTAssertEqual(inner.rightBoundary!.type, .boundary, str);
|
||||||
|
XCTAssertEqual(inner.rightBoundary!.nucleus, ")", str);
|
||||||
|
|
||||||
|
XCTAssertEqual((innerList.atoms.count), 1);
|
||||||
|
let table = innerList.atoms[0] as! MTMathTable
|
||||||
|
XCTAssertEqual(table.type, .table);
|
||||||
|
XCTAssertEqual(table.nucleus, "");
|
||||||
|
XCTAssertEqual(table.environment, "matrix");
|
||||||
|
XCTAssertEqual(table.interRowAdditionalSpacing, 0);
|
||||||
|
XCTAssertEqual(table.interColumnSpacing, 18);
|
||||||
|
XCTAssertEqual(table.numRows, 2);
|
||||||
|
XCTAssertEqual(table.numColumns, 2);
|
||||||
|
|
||||||
|
for i in 0..<2 {
|
||||||
|
let alignment = table.get(alignmentForColumn:i)
|
||||||
|
XCTAssertEqual(alignment, .center);
|
||||||
|
for j in 0..<2 {
|
||||||
|
let cell = table.cells[j][i];
|
||||||
|
XCTAssertEqual(cell.atoms.count, 2);
|
||||||
|
let style = cell.atoms[0] as! MTMathStyle
|
||||||
|
XCTAssertEqual(style.type, .style);
|
||||||
|
XCTAssertEqual(style.style, .text);
|
||||||
|
|
||||||
|
let atom = cell.atoms[1];
|
||||||
|
XCTAssertEqual(atom.type, .variable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert it back to latex
|
||||||
|
let latex = MTMathListBuilder.mathListToString(list)
|
||||||
|
XCTAssertEqual(latex, "\\left( \\begin{matrix}x&y\\\\ z&w\\end{matrix}\\right) ");
|
||||||
|
}
|
||||||
|
|
||||||
|
func testDefaultTable() throws {
|
||||||
|
let str = "x \\\\ y";
|
||||||
|
let list = MTMathListBuilder.build(fromString:str)!
|
||||||
|
|
||||||
|
XCTAssertNotNil(list);
|
||||||
|
XCTAssertEqual(list.atoms.count, 1);
|
||||||
|
let table = list.atoms[0] as! MTMathTable
|
||||||
|
XCTAssertEqual(table.type, .table);
|
||||||
|
XCTAssertEqual(table.nucleus, "");
|
||||||
|
XCTAssertNil(table.environment);
|
||||||
|
XCTAssertEqual(table.interRowAdditionalSpacing, 1);
|
||||||
|
XCTAssertEqual(table.interColumnSpacing, 0);
|
||||||
|
XCTAssertEqual(table.numRows, 2);
|
||||||
|
XCTAssertEqual(table.numColumns, 1);
|
||||||
|
|
||||||
|
for i in 0..<1 {
|
||||||
|
let alignment = table.get(alignmentForColumn: i)
|
||||||
|
XCTAssertEqual(alignment, .left);
|
||||||
|
for j in 0..<2 {
|
||||||
|
let cell = table.cells[j][i];
|
||||||
|
XCTAssertEqual(cell.atoms.count, 1);
|
||||||
|
let atom = cell.atoms[0];
|
||||||
|
XCTAssertEqual(atom.type, .variable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert it back to latex
|
||||||
|
let latex = MTMathListBuilder.mathListToString(list)
|
||||||
|
XCTAssertEqual(latex, "x\\\\ y");
|
||||||
|
}
|
||||||
|
|
||||||
|
func testDefaultTableWithCols() throws {
|
||||||
|
let str = "x & y \\\\ z & w";
|
||||||
|
let list = MTMathListBuilder.build(fromString:str)!
|
||||||
|
|
||||||
|
XCTAssertNotNil(list);
|
||||||
|
XCTAssertEqual((list.atoms.count), 1);
|
||||||
|
let table = list.atoms[0] as! MTMathTable
|
||||||
|
XCTAssertEqual(table.type, .table);
|
||||||
|
XCTAssertEqual(table.nucleus, "");
|
||||||
|
XCTAssertNil(table.environment);
|
||||||
|
XCTAssertEqual(table.interRowAdditionalSpacing, 1);
|
||||||
|
XCTAssertEqual(table.interColumnSpacing, 0);
|
||||||
|
XCTAssertEqual(table.numRows, 2);
|
||||||
|
XCTAssertEqual(table.numColumns, 2);
|
||||||
|
|
||||||
|
for i in 0..<2 {
|
||||||
|
let alignment = table.get(alignmentForColumn:i)
|
||||||
|
XCTAssertEqual(alignment, .left);
|
||||||
|
for j in 0..<2 {
|
||||||
|
let cell = table.cells[j][i];
|
||||||
|
XCTAssertEqual(cell.atoms.count, 1);
|
||||||
|
let atom = cell.atoms[0];
|
||||||
|
XCTAssertEqual(atom.type, .variable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert it back to latex
|
||||||
|
let latex = MTMathListBuilder.mathListToString(list)
|
||||||
|
XCTAssertEqual(latex, "x&y\\\\ z&w");
|
||||||
|
}
|
||||||
|
|
||||||
|
func testEqalign() throws {
|
||||||
|
let str1 = "\\begin{eqalign}x&y\\\\ z&w\\end{eqalign}";
|
||||||
|
let str2 = "\\begin{split}x&y\\\\ z&w\\end{split}";
|
||||||
|
let str3 = "\\begin{aligned}x&y\\\\ z&w\\end{aligned}";
|
||||||
|
for str in [str1, str2, str3] {
|
||||||
|
let list = MTMathListBuilder.build(fromString:str)!
|
||||||
|
|
||||||
|
XCTAssertNotNil(list);
|
||||||
|
XCTAssertEqual((list.atoms.count), 1);
|
||||||
|
let table = list.atoms[0] as! MTMathTable
|
||||||
|
XCTAssertEqual(table.type, .table);
|
||||||
|
XCTAssertEqual(table.nucleus, "");
|
||||||
|
XCTAssertEqual(table.interRowAdditionalSpacing, 1);
|
||||||
|
XCTAssertEqual(table.interColumnSpacing, 0);
|
||||||
|
XCTAssertEqual(table.numRows, 2);
|
||||||
|
XCTAssertEqual(table.numColumns, 2);
|
||||||
|
|
||||||
|
for i in 0..<2 {
|
||||||
|
let alignment = table.get(alignmentForColumn:i)
|
||||||
|
XCTAssertEqual(alignment, (i == 0) ? .right: .left);
|
||||||
|
for j in 0..<2 {
|
||||||
|
let cell = table.cells[j][i];
|
||||||
|
if (i == 0) {
|
||||||
|
XCTAssertEqual(cell.atoms.count, 1);
|
||||||
|
let atom = cell.atoms[0];
|
||||||
|
XCTAssertEqual(atom.type, .variable);
|
||||||
|
} else {
|
||||||
|
XCTAssertEqual(cell.atoms.count, 2);
|
||||||
|
self.checkAtomTypes(cell, types:[.ordinary, .variable], desc:str)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert it back to latex
|
||||||
|
let latex = MTMathListBuilder.mathListToString(list)
|
||||||
|
XCTAssertEqual(latex, str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testDisplayLines() throws {
|
||||||
|
let str1 = "\\begin{displaylines}x\\\\ y\\end{displaylines}";
|
||||||
|
let str2 = "\\begin{gather}x\\\\ y\\end{gather}";
|
||||||
|
for str in [str1, str2] {
|
||||||
|
let list = MTMathListBuilder.build(fromString:str)
|
||||||
|
|
||||||
|
XCTAssertNotNil(list)
|
||||||
|
XCTAssertEqual(list?.atoms.count, 1);
|
||||||
|
let table = list?.atoms[0] as! MTMathTable
|
||||||
|
XCTAssertEqual(table.type, .table);
|
||||||
|
XCTAssertEqual(table.nucleus, "");
|
||||||
|
XCTAssertEqual(table.interRowAdditionalSpacing, 1);
|
||||||
|
XCTAssertEqual(table.interColumnSpacing, 0);
|
||||||
|
XCTAssertEqual(table.numRows, 2);
|
||||||
|
XCTAssertEqual(table.numColumns, 1);
|
||||||
|
|
||||||
|
for i in 0..<1 {
|
||||||
|
let alignment = table.get(alignmentForColumn:i)
|
||||||
|
XCTAssertEqual(alignment, .center);
|
||||||
|
for j in 0..<2 {
|
||||||
|
let cell = table.cells[j][i];
|
||||||
|
XCTAssertEqual(cell.atoms.count, 1);
|
||||||
|
let atom = cell.atoms[0];
|
||||||
|
XCTAssertEqual(atom.type, .variable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert it back to latex
|
||||||
|
let latex = MTMathListBuilder.mathListToString(list)
|
||||||
|
XCTAssertEqual(latex, str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testErrors() throws {
|
||||||
|
let data = getTestDataParseErrors()
|
||||||
|
for testCase in data {
|
||||||
|
let str = testCase.0
|
||||||
|
var error = NSError()
|
||||||
|
let list = MTMathListBuilder.build(fromString: str, error:&error)
|
||||||
|
let desc = "Error for string:\(str)"
|
||||||
|
XCTAssertNil(list, desc)
|
||||||
|
XCTAssertNotNil(error, desc)
|
||||||
|
XCTAssertEqual(error.domain, MTParseError, desc)
|
||||||
|
let num = testCase.1
|
||||||
|
let code = num.rawValue
|
||||||
|
XCTAssertEqual(error.code, code, desc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testCustom() throws {
|
||||||
|
let str = "\\lcm(a,b)";
|
||||||
|
var error = NSError()
|
||||||
|
var list = MTMathListBuilder.build(fromString: str, error:&error)
|
||||||
|
XCTAssertNil(list);
|
||||||
|
XCTAssert(error.code == NSNotFound)
|
||||||
|
|
||||||
|
MTMathAtomFactory.add(latexSymbol: "lcm", value: MTMathAtomFactory.operatorWithName("lcm", limits:false))
|
||||||
|
list = MTMathListBuilder.build(fromString: str, error:&error)
|
||||||
|
let atomTypes = [MTMathAtomType.largeOperator, .open, .variable, .punctuation, .variable, .close]
|
||||||
|
self.checkAtomTypes(list, types:atomTypes, desc:"Error for lcm")
|
||||||
|
|
||||||
|
// convert it back to latex
|
||||||
|
let latex = MTMathListBuilder.mathListToString(list)
|
||||||
|
XCTAssertEqual(latex, "\\lcm (a,b)");
|
||||||
|
}
|
||||||
|
|
||||||
|
func testFontSingle() throws {
|
||||||
|
let str = "\\mathbf x";
|
||||||
|
let list = MTMathListBuilder.build(fromString: str)!
|
||||||
|
let desc = "Error for string:\(str)"
|
||||||
|
|
||||||
|
XCTAssertNotNil(list, desc)
|
||||||
|
XCTAssertEqual(list.atoms.count, 1, desc)
|
||||||
|
let atom = list.atoms[0];
|
||||||
|
XCTAssertEqual(atom.type, .variable, desc)
|
||||||
|
XCTAssertEqual(atom.nucleus, "x", desc)
|
||||||
|
XCTAssertEqual(atom.fontStyle, .bold)
|
||||||
|
|
||||||
|
// convert it back to latex
|
||||||
|
let latex = MTMathListBuilder.mathListToString(list)
|
||||||
|
XCTAssertEqual(latex, "\\mathbf{x}", desc)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testFontOneChar() throws {
|
||||||
|
let str = "\\cal xy";
|
||||||
|
let list = MTMathListBuilder.build(fromString: str)!
|
||||||
|
let desc = "Error for string:\(str)"
|
||||||
|
|
||||||
|
XCTAssertNotNil(list, desc)
|
||||||
|
XCTAssertEqual((list.atoms.count), 2, desc)
|
||||||
|
var atom = list.atoms[0];
|
||||||
|
XCTAssertEqual(atom.type, .variable, desc)
|
||||||
|
XCTAssertEqual(atom.nucleus, "x", desc)
|
||||||
|
XCTAssertEqual(atom.fontStyle, .caligraphic);
|
||||||
|
|
||||||
|
atom = list.atoms[1];
|
||||||
|
XCTAssertEqual(atom.type, .variable, desc)
|
||||||
|
XCTAssertEqual(atom.nucleus, "y", desc)
|
||||||
|
XCTAssertEqual(atom.fontStyle, .defaultStyle);
|
||||||
|
|
||||||
|
// convert it back to latex
|
||||||
|
let latex = MTMathListBuilder.mathListToString(list)
|
||||||
|
XCTAssertEqual(latex, "\\mathcal{x}y", desc)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testFontMultipleChars() throws {
|
||||||
|
let str = "\\frak{xy}";
|
||||||
|
let list = MTMathListBuilder.build(fromString: str)!
|
||||||
|
let desc = "Error for string:\(str)"
|
||||||
|
|
||||||
|
XCTAssertNotNil(list, desc)
|
||||||
|
XCTAssertEqual((list.atoms.count), 2, desc)
|
||||||
|
var atom = list.atoms[0];
|
||||||
|
XCTAssertEqual(atom.type, .variable, desc)
|
||||||
|
XCTAssertEqual(atom.nucleus, "x", desc)
|
||||||
|
XCTAssertEqual(atom.fontStyle, .fraktur);
|
||||||
|
|
||||||
|
atom = list.atoms[1];
|
||||||
|
XCTAssertEqual(atom.type, .variable, desc)
|
||||||
|
XCTAssertEqual(atom.nucleus, "y", desc)
|
||||||
|
XCTAssertEqual(atom.fontStyle, .fraktur);
|
||||||
|
|
||||||
|
// convert it back to latex
|
||||||
|
let latex = MTMathListBuilder.mathListToString(list)
|
||||||
|
XCTAssertEqual(latex, "\\mathfrak{xy}", desc)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testFontOneCharInside() throws {
|
||||||
|
let str = "\\sqrt \\mathrm x y";
|
||||||
|
let list = MTMathListBuilder.build(fromString: str)!
|
||||||
|
let desc = "Error for string:\(str)"
|
||||||
|
|
||||||
|
XCTAssertNotNil(list, desc)
|
||||||
|
XCTAssertEqual((list.atoms.count), 2, desc)
|
||||||
|
|
||||||
|
let rad = list.atoms[0] as! MTRadical
|
||||||
|
XCTAssertEqual(rad.type, .radical, desc)
|
||||||
|
XCTAssertEqual(rad.nucleus, "", desc)
|
||||||
|
|
||||||
|
let subList = rad.radicand!
|
||||||
|
var atom = subList.atoms[0];
|
||||||
|
XCTAssertEqual(atom.type, .variable, desc)
|
||||||
|
XCTAssertEqual(atom.nucleus, "x", desc)
|
||||||
|
XCTAssertEqual(atom.fontStyle, .roman);
|
||||||
|
|
||||||
|
atom = list.atoms[1];
|
||||||
|
XCTAssertEqual(atom.type, .variable, desc)
|
||||||
|
XCTAssertEqual(atom.nucleus, "y", desc)
|
||||||
|
XCTAssertEqual(atom.fontStyle, .defaultStyle)
|
||||||
|
|
||||||
|
// convert it back to latex
|
||||||
|
let latex = MTMathListBuilder.mathListToString(list)
|
||||||
|
XCTAssertEqual(latex, "\\sqrt{\\mathrm{x}}y", desc)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testText() throws {
|
||||||
|
let str = "\\text{x y}";
|
||||||
|
let list = MTMathListBuilder.build(fromString: str)!
|
||||||
|
let desc = "Error for string:\(str)"
|
||||||
|
|
||||||
|
XCTAssertNotNil(list, desc)
|
||||||
|
XCTAssertEqual((list.atoms.count), 3, desc)
|
||||||
|
var atom = list.atoms[0];
|
||||||
|
XCTAssertEqual(atom.type, .variable, desc)
|
||||||
|
XCTAssertEqual(atom.nucleus, "x", desc)
|
||||||
|
XCTAssertEqual(atom.fontStyle, .roman);
|
||||||
|
|
||||||
|
atom = list.atoms[1];
|
||||||
|
XCTAssertEqual(atom.type, .ordinary, desc)
|
||||||
|
XCTAssertEqual(atom.nucleus, " ", desc)
|
||||||
|
|
||||||
|
atom = list.atoms[2];
|
||||||
|
XCTAssertEqual(atom.type, .variable, desc)
|
||||||
|
XCTAssertEqual(atom.nucleus, "y", desc)
|
||||||
|
XCTAssertEqual(atom.fontStyle, .roman);
|
||||||
|
|
||||||
|
|
||||||
|
// convert it back to latex
|
||||||
|
let latex = MTMathListBuilder.mathListToString(list)
|
||||||
|
XCTAssertEqual(latex, "\\mathrm{x\\ y}", desc)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testLimits() throws {
|
||||||
|
// Int with no limits (default)
|
||||||
|
var str = "\\int";
|
||||||
|
var list = MTMathListBuilder.build(fromString: str)!
|
||||||
|
var desc = "Error for string:\(str)"
|
||||||
|
|
||||||
|
XCTAssertNotNil(list, desc)
|
||||||
|
XCTAssertEqual((list.atoms.count), 1, desc)
|
||||||
|
var op = list.atoms[0] as! MTLargeOperator
|
||||||
|
XCTAssertEqual(op.type, .largeOperator, desc)
|
||||||
|
XCTAssertFalse(op.limits);
|
||||||
|
|
||||||
|
// convert it back to latex
|
||||||
|
var latex = MTMathListBuilder.mathListToString(list)
|
||||||
|
XCTAssertEqual(latex, "\\int ", desc)
|
||||||
|
|
||||||
|
// Int with limits
|
||||||
|
str = "\\int\\limits"
|
||||||
|
list = MTMathListBuilder.build(fromString: str)!
|
||||||
|
desc = "Error for string:\(str)"
|
||||||
|
|
||||||
|
XCTAssertNotNil(list, desc)
|
||||||
|
XCTAssertEqual((list.atoms.count), 1, desc)
|
||||||
|
op = list.atoms[0] as! MTLargeOperator
|
||||||
|
XCTAssertEqual(op.type, .largeOperator, desc)
|
||||||
|
XCTAssertTrue(op.limits);
|
||||||
|
|
||||||
|
// convert it back to latex
|
||||||
|
latex = MTMathListBuilder.mathListToString(list)
|
||||||
|
XCTAssertEqual(latex, "\\int \\limits ", desc)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testNoLimits() throws {
|
||||||
|
// Sum with limits (default)
|
||||||
|
var str = "\\sum";
|
||||||
|
var list = MTMathListBuilder.build(fromString: str)!
|
||||||
|
var desc = "Error for string:\(str)"
|
||||||
|
|
||||||
|
XCTAssertNotNil(list, desc)
|
||||||
|
XCTAssertEqual((list.atoms.count), 1, desc)
|
||||||
|
var op = list.atoms[0] as! MTLargeOperator
|
||||||
|
XCTAssertEqual(op.type, .largeOperator, desc)
|
||||||
|
XCTAssertTrue(op.limits);
|
||||||
|
|
||||||
|
// convert it back to latex
|
||||||
|
var latex = MTMathListBuilder.mathListToString(list)
|
||||||
|
XCTAssertEqual(latex, "\\sum ", desc)
|
||||||
|
|
||||||
|
// Int with limits
|
||||||
|
str = "\\sum\\nolimits";
|
||||||
|
list = MTMathListBuilder.build(fromString: str)!
|
||||||
|
desc = "Error for string:\(str)"
|
||||||
|
|
||||||
|
XCTAssertNotNil(list, desc)
|
||||||
|
XCTAssertEqual(list.atoms.count, 1, desc)
|
||||||
|
op = list.atoms[0] as! MTLargeOperator
|
||||||
|
XCTAssertEqual(op.type, .largeOperator, desc)
|
||||||
|
XCTAssertFalse(op.limits);
|
||||||
|
|
||||||
|
// convert it back to latex
|
||||||
|
latex = MTMathListBuilder.mathListToString(list)
|
||||||
|
XCTAssertEqual(latex, "\\sum \\nolimits ", desc)
|
||||||
|
}
|
||||||
|
|
||||||
// func testPerformanceExample() throws {
|
// func testPerformanceExample() throws {
|
||||||
// // This is an example of a performance test case.
|
// // This is an example of a performance test case.
|
||||||
|
|||||||
Reference in New Issue
Block a user