Passed complete MTMathListBuilderTest suite.

This commit is contained in:
Michael Griebling
2023-01-08 08:23:03 -05:00
parent 9afc6970d4
commit aef877099e
5 changed files with 229 additions and 143 deletions

View File

@@ -98,8 +98,8 @@ public class MTMathAtomFactory {
"widetilde" : "\u{0303}" "widetilde" : "\u{0303}"
] ]
var _accentValueToName: [String: String]? = nil static var _accentValueToName: [String: String]? = nil
public var accentValueToName: [String: String] { public static var accentValueToName: [String: String] {
if _accentValueToName == nil { if _accentValueToName == nil {
var output = [String: String]() var output = [String: String]()
@@ -113,17 +113,14 @@ public class MTMathAtomFactory {
} }
} }
} }
output[value] = key output[value] = key
} }
_accentValueToName = output _accentValueToName = output
} }
return _accentValueToName! return _accentValueToName!
} }
public var supportedLatexSymbols: [String: MTMathAtom] = [ public static var supportedLatexSymbols: [String: MTMathAtom] = [
"square" : MTMathAtomFactory.placeholder(), "square" : MTMathAtomFactory.placeholder(),
// Greek characters // Greek characters
@@ -386,14 +383,12 @@ public class MTMathAtomFactory {
"scriptscriptstyle" : MTMathStyle(style: .scriptOfScript), "scriptscriptstyle" : MTMathStyle(style: .scriptOfScript),
] ]
var latexSymbolNames = [String]()
var _textToLatexSymbolName: [String: String]? = nil var _textToLatexSymbolName: [String: String]? = nil
public var textToLatexSymbolName: [String: String] { public var textToLatexSymbolName: [String: String] {
get { get {
if self._textToLatexSymbolName == nil { if self._textToLatexSymbolName == nil {
var output = [String: String]() var output = [String: String]()
for (key, atom) in self.supportedLatexSymbols { for (key, atom) in Self.supportedLatexSymbols {
if atom.nucleus.count == 0 { if atom.nucleus.count == 0 {
continue continue
} }
@@ -423,35 +418,32 @@ public class MTMathAtomFactory {
public static let sharedInstance = MTMathAtomFactory() public static let sharedInstance = MTMathAtomFactory()
static let fontStyles : [String: MTFontStyle] = [ static let fontStyles : [String: MTFontStyle] = [
"mathnormal" : (.defaultStyle), "mathnormal" : .defaultStyle,
"mathrm": (.roman), "mathrm": .roman,
"textrm": (.roman), "textrm": .roman,
"rm": (.roman), "rm": .roman,
"mathbf": (.bold), "mathbf": .bold,
"bf": (.bold), "bf": .bold,
"textbf": (.bold), "textbf": .bold,
"mathcal": (.caligraphic), "mathcal": .caligraphic,
"cal": (.caligraphic), "cal": .caligraphic,
"mathtt": (.typewriter), "mathtt": .typewriter,
"texttt": (.typewriter), "texttt": .typewriter,
"mathit": (.italic), "mathit": .italic,
"textit": (.italic), "textit": .italic,
"mit": (.italic), "mit": .italic,
"mathsf": (.sansSerif), "mathsf": .sansSerif,
"textsf": (.sansSerif), "textsf": .sansSerif,
"mathfrak": (.fraktur), "mathfrak": .fraktur,
"frak": (.fraktur), "frak": .fraktur,
"mathbb": (.blackboard), "mathbb": .blackboard,
"mathbfit": (.boldItalic), "mathbfit": .boldItalic,
"bm": (.boldItalic), "bm": .boldItalic,
"text": (.roman), "text": .roman,
] ]
public static func fontStyleWithName(_ fontName:String) -> MTFontStyle? { public static func fontStyleWithName(_ fontName:String) -> MTFontStyle? {
if let style = fontStyles[fontName] { return fontStyles[fontName]
return style
}
return nil
} }
public static func fontNameForStyle(_ fontStyle:MTFontStyle) -> String { public static func fontNameForStyle(_ fontStyle:MTFontStyle) -> String {
@@ -600,13 +592,15 @@ public class MTMathAtomFactory {
If the latex symbol is unknown this will return nil. This supports LaTeX aliases as well. If the latex symbol is unknown this will return nil. This supports LaTeX aliases as well.
*/ */
public static func atom(forLatexSymbol name: String) -> MTMathAtom? { public static func atom(forLatexSymbol name: String) -> MTMathAtom? {
var _name = name var name = name
if let canonicalName = aliases[name] { if let canonicalName = aliases[name] {
_name = canonicalName name = canonicalName
} }
if let atom = sharedInstance.supportedLatexSymbols[_name] { if let atom = supportedLatexSymbols[name] {
if name == "int" { return MTMathAtomFactory.operatorWithName( "\u{222B}", limits: false) }
if name == "sum" { return MTMathAtomFactory.operatorWithName( "\u{2211}", limits: true) }
return atom return atom
} }
@@ -626,7 +620,7 @@ public class MTMathAtomFactory {
return nil return nil
} }
return Self.sharedInstance.textToLatexSymbolName[atom.nucleus] return sharedInstance.textToLatexSymbolName[atom.nucleus]
} }
/** Define a latex symbol for rendering. This function allows defining custom symbols that are /** Define a latex symbol for rendering. This function allows defining custom symbols that are
@@ -635,12 +629,12 @@ public class MTMathAtomFactory {
`[MTMathAtomFactory addLatexSymbol:@"lcm" value:[MTMathAtomFactory operatorWithName:@"lcm" limits: false)]` */ `[MTMathAtomFactory addLatexSymbol:@"lcm" value:[MTMathAtomFactory operatorWithName:@"lcm" limits: false)]` */
public static func add(latexSymbol name: String, value: MTMathAtom) { public static func add(latexSymbol name: String, value: MTMathAtom) {
Self.sharedInstance.supportedLatexSymbols[name] = value supportedLatexSymbols[name] = value
Self.sharedInstance.textToLatexSymbolName[value.nucleus] = name 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 displayed differently. */
public static func operatorWithName(_ 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)
} }
@@ -650,7 +644,7 @@ public class MTMathAtomFactory {
returns nil. The `innerList` of the returned `MTAccent` is nil. returns nil. The `innerList` of the returned `MTAccent` is nil.
*/ */
public static func accent(withName name: String) -> MTAccent? { public static func accent(withName name: String) -> MTAccent? {
if let accentValue = Self.accents[name] { if let accentValue = accents[name] {
return MTAccent(value: accentValue) return MTAccent(value: accentValue)
} }
return nil return nil
@@ -659,7 +653,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 accentName(_ accent: MTAccent) -> String? { public static func accentName(_ accent: MTAccent) -> String? {
return Self.sharedInstance.accentValueToName[accent.nucleus] return accentValueToName[accent.nucleus]
} }
/** Creates a new boundary atom for the given delimiter name. If the delimiter name /** Creates a new boundary atom for the given delimiter name. If the delimiter name
@@ -765,7 +759,7 @@ public class MTMathAtomFactory {
} else if env == "eqalign" || env == "split" || env == "aligned" { } else if env == "eqalign" || env == "split" || env == "aligned" {
if table.numColumns != 2 { if table.numColumns != 2 {
let message = "\(env) environment can only have 2 columns" let message = "\(env) environment can only have 2 columns"
if error != nil { if error == nil {
error = NSError(domain: MTParseError, code: MTParseErrors.invalidNumColumns.rawValue, userInfo: [NSLocalizedDescriptionKey:message]) error = NSError(domain: MTParseError, code: MTParseErrors.invalidNumColumns.rawValue, userInfo: [NSLocalizedDescriptionKey:message])
} }
return nil return nil
@@ -789,7 +783,7 @@ public class MTMathAtomFactory {
} else if env == "displaylines" || env == "gather" { } else if env == "displaylines" || env == "gather" {
if table.numColumns != 1 { if table.numColumns != 1 {
let message = "\(env) environment can only have 1 column" let message = "\(env) environment can only have 1 column"
if error != nil { if error == nil {
error = NSError(domain: MTParseError, code: MTParseErrors.invalidNumColumns.rawValue, userInfo: [NSLocalizedDescriptionKey:message]) error = NSError(domain: MTParseError, code: MTParseErrors.invalidNumColumns.rawValue, userInfo: [NSLocalizedDescriptionKey:message])
} }
return nil return nil
@@ -804,7 +798,7 @@ public class MTMathAtomFactory {
} else if env == "eqnarray" { } else if env == "eqnarray" {
if table.numColumns != 3 { if table.numColumns != 3 {
let message = "\(env) environment can only have 3 columns" let message = "\(env) environment can only have 3 columns"
if error != nil { if error == nil {
error = NSError(domain: MTParseError, code: MTParseErrors.invalidNumColumns.rawValue, userInfo: [NSLocalizedDescriptionKey:message]) error = NSError(domain: MTParseError, code: MTParseErrors.invalidNumColumns.rawValue, userInfo: [NSLocalizedDescriptionKey:message])
} }
return nil return nil
@@ -821,7 +815,7 @@ public class MTMathAtomFactory {
} else if env == "cases" { } else if env == "cases" {
if table.numColumns != 2 { if table.numColumns != 2 {
let message = "cases environment can only have 2 columns" let message = "cases environment can only have 2 columns"
if error != nil { if error == nil {
error = NSError(domain: MTParseError, code: MTParseErrors.invalidNumColumns.rawValue, userInfo: [NSLocalizedDescriptionKey:message]) error = NSError(domain: MTParseError, code: MTParseErrors.invalidNumColumns.rawValue, userInfo: [NSLocalizedDescriptionKey:message])
} }
return nil return nil
@@ -852,9 +846,7 @@ public class MTMathAtomFactory {
return inner return inner
} else { } else {
let message = "Unknown environment \(env)" let message = "Unknown environment \(env)"
if error != nil { error = NSError(domain: MTParseError, code: MTParseErrors.invalidEnv.rawValue, userInfo: [NSLocalizedDescriptionKey:message])
error = NSError(domain: MTParseError, code: MTParseErrors.invalidNumColumns.rawValue, userInfo: [NSLocalizedDescriptionKey:message])
}
return nil return nil
} }
} }

View File

@@ -78,13 +78,29 @@ public enum MTFontStyle:Int {
boldItalic boldItalic
} }
public class MTMathAtom: CustomStringConvertible { public class MTMathAtom: NSObject, NSCopying {
public var type: MTMathAtomType public var type: MTMathAtomType
public var subScript: MTMathList? public var subScript: MTMathList?
public var superScript: MTMathList? public var superScript: MTMathList?
public var nucleus: String = ""
public var childAtoms = [MTMathAtom]() // atoms that fused to create this one
public var indexRange = NSRange(location: 0, length: 0) // indexRange in list that this atom tracks:
var fontStyle: MTFontStyle = .defaultStyle var fontStyle: MTFontStyle = .defaultStyle
var fusedAtoms: MTMathList? var fusedAtoms: MTMathList?
public func copy(with zone: NSZone? = nil) -> Any {
let atom = MTMathAtom.atom(withType: type, value: nucleus)
atom.type = self.type
atom.nucleus = self.nucleus
atom.subScript = self.subScript?.copy() as? MTMathList
atom.superScript = self.subScript?.copy() as? MTMathList
atom.indexRange = self.indexRange
atom.fontStyle = self.fontStyle
return atom
}
public static func atom(withType type:MTMathAtomType, value:String) -> MTMathAtom { public static func atom(withType type:MTMathAtomType, value:String) -> MTMathAtom {
switch type { switch type {
case .largeOperator: case .largeOperator:
@@ -118,8 +134,7 @@ public class MTMathAtom: CustomStringConvertible {
if self.isScriptAllowed() { if self.isScriptAllowed() {
self.superScript = list self.superScript = list
} else { } else {
print("superscripts not allowed for atom \(self.type.rawValue)") NSException(name: NSExceptionName(rawValue: "Error"), reason: "Superscripts not allowed for atom \(self.type.rawValue)").raise()
self.superScript = nil
} }
} }
@@ -127,12 +142,11 @@ public class MTMathAtom: CustomStringConvertible {
if self.isScriptAllowed() { if self.isScriptAllowed() {
self.subScript = list self.subScript = list
} else { } else {
print("subscripts not allowed for atom \(self.type.rawValue)") NSException(name: NSExceptionName(rawValue: "Error"), reason: "Subscripts not allowed for atom \(self.type.rawValue)").raise()
self.subScript = nil
} }
} }
public var description: String { public override var description: String {
var string = "" var string = ""
string += self.nucleus string += self.nucleus
@@ -146,7 +160,6 @@ public class MTMathAtom: CustomStringConvertible {
return string return string
} }
public var nucleus: String = ""
public var finalized: MTMathAtom { public var finalized: MTMathAtom {
let finalized = self let finalized = self
if finalized.superScript != nil { if finalized.superScript != nil {
@@ -158,12 +171,6 @@ public class MTMathAtom: CustomStringConvertible {
return finalized return finalized
} }
// atoms that fused to create this one
public var childAtoms = [MTMathAtom]()
// indexRange in list that this atom tracks:
public var indexRange = NSRange(location: 0, length: 0)
public var string:String { public var string:String {
var str = self.nucleus var str = self.nucleus
if let superScript = self.superScript { if let superScript = self.superScript {
@@ -183,7 +190,7 @@ public class MTMathAtom: CustomStringConvertible {
self.superScript == nil, self.superScript == nil,
self.type == atom.type self.type == atom.type
else { else {
print("Can't fuse these 2 atom") print("Can't fuse these 2 atoms")
return return
} }
@@ -228,6 +235,16 @@ public class MTFraction: MTMathAtom {
public var numerator: MTMathList? = MTMathList() public var numerator: MTMathList? = MTMathList()
public var denominator: MTMathList? = MTMathList() public var denominator: MTMathList? = MTMathList()
public override func copy(with zone: NSZone? = nil) -> Any {
let frac = super.copy(with: zone) as! MTFraction
frac.numerator = self.numerator?.copy() as? MTMathList
frac.denominator = self.denominator?.copy() as? MTMathList
frac.hasRule = self.hasRule
frac.leftDelimiter = self.leftDelimiter
frac.rightDelimiter = self.rightDelimiter
return frac
}
override public var description: String { override public var description: String {
var string = "" var string = ""
if self.hasRule { if self.hasRule {
@@ -280,6 +297,13 @@ public class MTRadical: MTMathAtom {
self.init(type: .radical, value: "") self.init(type: .radical, value: "")
} }
public override func copy(with zone: NSZone? = nil) -> Any {
let rad = super.copy(with: zone) as! MTRadical
rad.radicand = self.radicand?.copy() as? MTMathList
rad.degree = self.degree?.copy() as? MTMathList
return rad
}
override public var description: String { override public var description: String {
var string = "\\sqrt" var string = "\\sqrt"
@@ -314,9 +338,16 @@ public class MTRadical: MTMathAtom {
public class MTLargeOperator: MTMathAtom { public class MTLargeOperator: MTMathAtom {
public var limits: Bool = false public var limits: Bool = false
convenience init(value: String, limits: Bool = false) { init(value: String, limits: Bool) {
self.init(type: .largeOperator, value: value) super.init(type: .largeOperator, value: value)
self.limits = limits self.limits = limits
//print("Operator \(value) limits:\(limits)")
}
public override func copy(with zone: NSZone? = nil) -> Any {
let op = super.copy(with: zone) as! MTLargeOperator
op.limits = self.limits
return op
} }
} }
@@ -339,6 +370,14 @@ public class MTInner: MTMathAtom {
} }
} }
public override func copy(with zone: NSZone? = nil) -> Any {
let inner = super.copy(with: zone) as! MTInner
inner.innerList = self.innerList?.copy() as? MTMathList
inner.leftBoundary = self.leftBoundary?.copy() as? MTMathAtom
inner.rightBoundary = self.rightBoundary?.copy() as? MTMathAtom
return inner
}
init() { init() {
super.init(type: .inner, value: "") super.init(type: .inner, value: "")
} }
@@ -375,9 +414,7 @@ public class MTInner: MTMathAtom {
override public var finalized: MTMathAtom { override public var finalized: MTMathAtom {
let finalized: MTInner = super.finalized as! MTInner let finalized: MTInner = super.finalized as! MTInner
finalized.innerList = finalized.innerList?.finalized finalized.innerList = finalized.innerList?.finalized
return finalized return finalized
} }
} }
@@ -387,12 +424,16 @@ public class MTOverLine: MTMathAtom {
override public var finalized: MTMathAtom { override public var finalized: MTMathAtom {
let finalized: MTOverLine = super.finalized as! MTOverLine let finalized: MTOverLine = super.finalized as! MTOverLine
finalized.innerList = finalized.innerList?.finalized finalized.innerList = finalized.innerList?.finalized
return finalized return finalized
} }
public override func copy(with zone: NSZone? = nil) -> Any {
let op = super.copy(with: zone) as! MTOverLine
op.innerList = self.innerList?.copy() as? MTMathList
return op
}
convenience init() { convenience init() {
self.init(type: .overline, value: "") self.init(type: .overline, value: "")
} }
@@ -403,12 +444,16 @@ public class MTUnderLine: MTMathAtom {
override public var finalized: MTMathAtom { override public var finalized: MTMathAtom {
let finalized: MTUnderLine = super.finalized as! MTUnderLine let finalized: MTUnderLine = super.finalized as! MTUnderLine
finalized.innerList = finalized.innerList?.finalized finalized.innerList = finalized.innerList?.finalized
return finalized return finalized
} }
public override func copy(with zone: NSZone? = nil) -> Any {
let op = super.copy(with: zone) as! MTUnderLine
op.innerList = self.innerList?.copy() as? MTMathList
return op
}
convenience init() { convenience init() {
self.init(type: .underline, value: "") self.init(type: .underline, value: "")
} }
@@ -419,12 +464,16 @@ public class MTAccent: MTMathAtom {
override public var finalized: MTMathAtom { override public var finalized: MTMathAtom {
let finalized: MTAccent = super.finalized as! MTAccent let finalized: MTAccent = super.finalized as! MTAccent
finalized.innerList = finalized.innerList?.finalized finalized.innerList = finalized.innerList?.finalized
return finalized return finalized
} }
public override func copy(with zone: NSZone? = nil) -> Any {
let op = super.copy(with: zone) as! MTAccent
op.innerList = self.innerList?.copy() as? MTMathList
return op
}
convenience init(value: String) { convenience init(value: String) {
self.init(type: .accent, value: value) self.init(type: .accent, value: value)
} }
@@ -437,6 +486,13 @@ public class MTMathSpace: MTMathAtom {
self.init(type: .space, value: "") self.init(type: .space, value: "")
self.space = space self.space = space
} }
public override func copy(with zone: NSZone? = nil) -> Any {
let op = super.copy(with: zone) as! MTMathSpace
op.space = self.space
return op
}
} }
public enum MTLineStyle { public enum MTLineStyle {
@@ -466,6 +522,12 @@ public class MTMathStyle: MTMathAtom {
self.init(type: .style, value: "") self.init(type: .style, value: "")
self.style = style self.style = style
} }
public override func copy(with zone: NSZone? = nil) -> Any {
let op = super.copy(with: zone) as! MTMathStyle
op.style = self.style
return op
}
} }
public class MTMathColor: MTMathAtom { public class MTMathColor: MTMathAtom {
@@ -484,6 +546,13 @@ public class MTMathColor: MTMathAtom {
self.init() self.init()
} }
public override func copy(with zone: NSZone? = nil) -> Any {
let op = super.copy(with: zone) as! MTMathColor
op.colorString = self.colorString
op.innerList = self.innerList?.copy() as? MTMathList
return op
}
public override var string: String { public override var string: String {
"\\color{\(self.colorString)}{\(self.innerList!.string)}" "\\color{\(self.colorString)}{\(self.innerList!.string)}"
} }
@@ -505,6 +574,13 @@ public class MTMathColorbox: MTMathAtom {
self.init() self.init()
} }
public override func copy(with zone: NSZone? = nil) -> Any {
let op = super.copy(with: zone) as! MTMathColorbox
op.colorString = self.colorString
op.innerList = self.innerList?.copy() as? MTMathList
return op
}
public override var string: String { public override var string: String {
"\\colorbox{\(self.colorString)}{\(self.innerList!.string)}" "\\colorbox{\(self.colorString)}{\(self.innerList!.string)}"
} }
@@ -523,8 +599,6 @@ public class MTMathTable: MTMathAtom {
public var environment: String? public var environment: String?
public var interColumnSpacing: CGFloat = 0 public var interColumnSpacing: CGFloat = 0
public var interRowAdditionalSpacing: CGFloat = 0 public var interRowAdditionalSpacing: CGFloat = 0
// public var numColumns = 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,6 +617,21 @@ public class MTMathTable: MTMathAtom {
self.environment = environment self.environment = environment
} }
public override func copy(with zone: NSZone? = nil) -> Any {
let op = super.copy(with: zone) as! MTMathTable
op.interRowAdditionalSpacing = self.interRowAdditionalSpacing
op.interColumnSpacing = self.interColumnSpacing
op.environment = self.environment
var cellCopy = [[MTMathList]]()
cellCopy.reserveCapacity(self.cells.count)
for row in self.cells {
let newRow = [MTMathList](row)
cellCopy.append(newRow)
}
op.cells = cellCopy
return op
}
public 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 {
@@ -592,12 +681,19 @@ public class MTMathTable: MTMathAtom {
} }
// represent list of math objects // represent list of math objects
extension MTMathList: CustomStringConvertible { extension MTMathList {
public var description: String { self.atoms.description } public override var description: String { self.atoms.description }
public var string: String { self.description } public var string: String { self.description }
} }
public class MTMathList { public class MTMathList: NSObject, NSCopying {
public func copy(with zone: NSZone? = nil) -> Any {
let list = MTMathList()
list.atoms = [MTMathAtom](self.atoms)
return list
}
public var atoms = [MTMathAtom]() public var atoms = [MTMathAtom]()
public var finalized: MTMathList { public var finalized: MTMathList {
@@ -658,7 +754,7 @@ public class MTMathList {
self.atoms.append(atom) self.atoms.append(atom)
} }
public init() { public override init() {
self.atoms = [] self.atoms = []
} }
@@ -666,7 +762,7 @@ public class MTMathList {
if self.isAtomAllowed(atom) { if self.isAtomAllowed(atom) {
self.atoms.append(atom) self.atoms.append(atom)
} else { } else {
print("error, cannot add atom of type \(atom.type.rawValue) into atomlist") NSException(name: NSExceptionName(rawValue: "Error"), reason: "Cannot add atom of type \(atom.type.rawValue) into mathlist").raise()
} }
} }
@@ -674,7 +770,7 @@ public class MTMathList {
if self.isAtomAllowed(atom) { if self.isAtomAllowed(atom) {
self.atoms.insert(atom, at: index) self.atoms.insert(atom, at: index)
} else { } else {
print("error, cannot add atom of type \(atom.type.rawValue) into atomlist") NSException(name: NSExceptionName(rawValue: "Error"), reason: "Cannot add atom of type \(atom.type.rawValue) into mathlist").raise()
} }
} }

View File

@@ -99,16 +99,16 @@ public class MTMathListBuilder {
self.spacesAllowed = false self.spacesAllowed = false
} }
func build() -> MTMathList? { public func build() -> MTMathList? {
if let list = self.buildInternal(false) { let list = self.buildInternal(false)
if self.hasCharacters { if self.hasCharacters && error == nil {
print("Mismatched braces: \(self.string)") self.setError(.mismatchBraces, message: "Mismatched braces: \(self.string)")
return nil
}
if error != nil {
return nil return nil
} }
return list return list
} else {
return nil
}
} }
public static func build(fromString string: String) -> MTMathList? { public static func build(fromString string: String) -> MTMathList? {
@@ -116,14 +116,12 @@ 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 {
error = builder.error! error = builder.error
return nil return nil
} else {
error = NSError()
} }
return output return output
} }
@@ -213,9 +211,7 @@ public class MTMathListBuilder {
cell.atoms.removeFirst() cell.atoms.removeFirst()
} }
} }
str += mathListToString(cell) str += mathListToString(cell)
if j < row.count - 1 { if j < row.count - 1 {
str += "&" str += "&"
} }
@@ -224,7 +220,6 @@ public class MTMathListBuilder {
str += "\\\\ " str += "\\\\ "
} }
} }
if table.environment != nil { if table.environment != nil {
str += "\\end{\(table.environment!)}" str += "\\end{\(table.environment!)}"
} }
@@ -331,11 +326,11 @@ public class MTMathListBuilder {
} }
} }
func buildInternal(_ oneCharOnly: Bool) -> MTMathList? { public func buildInternal(_ oneCharOnly: Bool) -> MTMathList? {
return self.buildInternal(oneCharOnly, stopChar: nil) return self.buildInternal(oneCharOnly, stopChar: nil)
} }
func buildInternal(_ oneCharOnly: Bool, stopChar stop: Character?) -> MTMathList? { public 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
@@ -391,8 +386,7 @@ public class MTMathListBuilder {
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.
let errorMessage = "Mismatched braces." self.setError(.mismatchBraces, message:"Mismatched braces.")
self.setError(.mismatchBraces, message:errorMessage)
return nil return nil
} else if char == "\\" { } else if char == "\\" {
let command = readCommand() let command = readCommand()
@@ -607,8 +601,7 @@ public class MTMathListBuilder {
} }
} }
func stopCommand(_ command: String, list:MTMathList, stopChar:Character?) -> MTMathList? { static var fractionCommands: [String:[Character]] {
var fractionCommands: [String:[Character]] {
[ [
"over": [], "over": [],
"atop" : [], "atop" : [],
@@ -617,6 +610,8 @@ public class MTMathListBuilder {
"brace" : [ "{", "}"] "brace" : [ "{", "}"]
] ]
} }
func stopCommand(_ command: String, list:MTMathList, stopChar:Character?) -> MTMathList? {
if command == "right" { if command == "right" {
if currentInnerAtom == nil { if currentInnerAtom == nil {
let errorMessage = "Missing \\left"; let errorMessage = "Missing \\left";
@@ -629,7 +624,7 @@ public class MTMathListBuilder {
} }
// return the list read so far. // return the list read so far.
return list return list
} else if let delims = fractionCommands[command] { } else if let delims = Self.fractionCommands[command] {
var frac:MTFraction! = nil; var frac:MTFraction! = nil;
if command == "over" { if command == "over" {
frac = MTFraction() frac = MTFraction()
@@ -662,11 +657,11 @@ public class MTMathListBuilder {
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
} }
let env = self.readEnvironment() let env = self.readEnvironment()
if env == nil { if env == nil {
return nil; return nil
} }
if env! != currentEnv!.envName { if env! != currentEnv!.envName {
let errorMessage = "Begin environment name \(currentEnv!.envName!) does not match end name: \(env!)" let errorMessage = "Begin environment name \(currentEnv!.envName!) does not match end name: \(env!)"
@@ -683,7 +678,7 @@ public class MTMathListBuilder {
// Applies the modifier to the atom. Returns true if modifier applied. // Applies the modifier to the atom. Returns true if modifier applied.
func applyModifier(_ modifier:String, atom:MTMathAtom?) -> Bool { func applyModifier(_ modifier:String, atom:MTMathAtom?) -> Bool {
if modifier == "limits" { if modifier == "limits" {
if atom!.type != .largeOperator { if atom?.type != .largeOperator {
let errorMessage = "Limits can only be applied to an operator." let errorMessage = "Limits can only be applied to an operator."
self.setError(.invalidLimits, message:errorMessage) self.setError(.invalidLimits, message:errorMessage)
} else { } else {
@@ -695,14 +690,13 @@ public class MTMathListBuilder {
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
} else { } else {
let op = atom as! MTLargeOperator let op = atom as! MTLargeOperator
op.limits = false op.limits = false
} }
return true; return true
} }
return false; return false
} }
func setError(_ code:MTParseErrors, message:String) { func setError(_ code:MTParseErrors, message:String) {
@@ -795,7 +789,7 @@ public class MTMathListBuilder {
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.
print("Missing {") self.setError(.characterNotFound, message: "Missing {")
return nil return nil
} }
@@ -804,7 +798,7 @@ public class MTMathListBuilder {
if !self.expectCharacter("}") { if !self.expectCharacter("}") {
// We didn"t find an closing brace, so invalid format. // We didn"t find an closing brace, so invalid format.
print("Missing {") self.setError(.characterNotFound, message: "Missing }")
return nil; return nil;
} }
return env return env
@@ -872,7 +866,7 @@ public class MTMathListBuilder {
return nil return nil
} }
var error:NSError? var error:NSError? = self.error
let table = MTMathAtomFactory.table(withEnvironment: currentEnv?.envName, rows: rows, error: &error) let table = MTMathAtomFactory.table(withEnvironment: currentEnv?.envName, rows: rows, error: &error)
if table == nil && self.error == nil { if table == nil && self.error == nil {
self.error = error self.error = error
@@ -892,7 +886,7 @@ public class MTMathListBuilder {
let boundary = MTMathAtomFactory.boundary(forDelimiter: delim!) let boundary = MTMathAtomFactory.boundary(forDelimiter: delim!)
if boundary == nil { if boundary == nil {
let errorMessage = "Invalid delimiter for \(delimiterType): \(delim!)" let errorMessage = "Invalid delimiter for \(delimiterType): \(delim!)"
self.setError(.missingDelimiter, message:errorMessage) self.setError(.invalidDelimiter, message:errorMessage)
return nil return nil
} }
return boundary return boundary

View File

@@ -71,12 +71,12 @@ class MTMathUILabel : MTView {
var latex = "" { var latex = "" {
didSet { didSet {
self.error = nil self.error = nil
var error = NSError() var error : NSError? = nil
self.mathList = MTMathListBuilder.build(fromString: latex, error: &error) self.mathList = MTMathListBuilder.build(fromString: latex, error: &error)
if error != NSError() { if error != nil {
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 {

View File

@@ -195,9 +195,9 @@ final class SwiftMathRenderTests: XCTestCase {
let data = getTestData() let data = getTestData()
for testCase in data { for testCase in data {
let str = testCase.build let str = testCase.build
var error = NSError() var error : NSError? = nil
let list = MTMathListBuilder.build(fromString: str, error: &error) let list = MTMathListBuilder.build(fromString: str, error: &error)
XCTAssert(error.code == 0) XCTAssertNil(error)
let desc = "Error for string:\(str)" let desc = "Error for string:\(str)"
let atomTypes = testCase.atomType let atomTypes = testCase.atomType
self.checkAtomTypes(list, types:atomTypes, desc:desc) self.checkAtomTypes(list, types:atomTypes, desc:desc)
@@ -212,9 +212,9 @@ final class SwiftMathRenderTests: XCTestCase {
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? = nil
let list = MTMathListBuilder.build(fromString: str, error:&error) let list = MTMathListBuilder.build(fromString: str, error:&error)
XCTAssert(error.code == NSNotFound) XCTAssertNil(error)
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)
@@ -246,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? = nil
let list = MTMathListBuilder.build(fromString: str, error:&error) let list = MTMathListBuilder.build(fromString: str, error:&error)
XCTAssert(error.code == NSNotFound) XCTAssertNil(error)
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)
@@ -280,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? = nil
let list = MTMathListBuilder.build(fromString: str, error:&error) let list = MTMathListBuilder.build(fromString: str, error:&error)
XCTAssert(error.code == NSNotFound) XCTAssertNil(error)
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)
@@ -508,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? = nil
let list = MTMathListBuilder.build(fromString: str, error: &error)! let list = MTMathListBuilder.build(fromString: str, error: &error)!
XCTAssertNotNil(list, str); XCTAssertNotNil(list, str);
@@ -1149,26 +1149,30 @@ final class SwiftMathRenderTests: XCTestCase {
let data = getTestDataParseErrors() let data = getTestDataParseErrors()
for testCase in data { for testCase in data {
let str = testCase.0 let str = testCase.0
var error = NSError() var error : NSError? = nil
if str == "\\begin{displaylines} x & y \\end{displaylines}" {
let x = 0
}
let list = MTMathListBuilder.build(fromString: str, error:&error) let list = MTMathListBuilder.build(fromString: str, error:&error)
let desc = "Error for string:\(str)" let desc = "Error for string:\(str)"
XCTAssertNil(list, desc) XCTAssertNil(list, desc)
XCTAssertNotNil(error, desc) XCTAssertNotNil(error, desc)
XCTAssertEqual(error.domain, MTParseError, desc) XCTAssertEqual(error!.domain, MTParseError, desc)
let code = error!.code
let num = testCase.1 let num = testCase.1
let code = num.rawValue XCTAssertEqual(error!.code, num.rawValue, desc)
XCTAssertEqual(error.code, code, desc)
} }
} }
func testCustom() throws { func testCustom() throws {
let str = "\\lcm(a,b)"; let str = "\\lcm(a,b)";
var error = NSError() var error : NSError? = nil
var list = MTMathListBuilder.build(fromString: str, error:&error) var list = MTMathListBuilder.build(fromString: str, error:&error)
XCTAssertNil(list); XCTAssertNil(list)
XCTAssert(error.code == NSNotFound) XCTAssertNotNil(error)
MTMathAtomFactory.add(latexSymbol: "lcm", value: MTMathAtomFactory.operatorWithName("lcm", limits:false)) MTMathAtomFactory.add(latexSymbol: "lcm", value: MTMathAtomFactory.operatorWithName("lcm", limits:false))
error = nil
list = MTMathListBuilder.build(fromString: str, error:&error) list = MTMathListBuilder.build(fromString: str, error:&error)
let atomTypes = [MTMathAtomType.largeOperator, .open, .variable, .punctuation, .variable, .close] let atomTypes = [MTMathAtomType.largeOperator, .open, .variable, .punctuation, .variable, .close]
self.checkAtomTypes(list, types:atomTypes, desc:"Error for lcm") self.checkAtomTypes(list, types:atomTypes, desc:"Error for lcm")
@@ -1319,7 +1323,7 @@ final class SwiftMathRenderTests: XCTestCase {
XCTAssertEqual((list.atoms.count), 1, desc) XCTAssertEqual((list.atoms.count), 1, desc)
op = list.atoms[0] as! MTLargeOperator op = list.atoms[0] as! MTLargeOperator
XCTAssertEqual(op.type, .largeOperator, desc) XCTAssertEqual(op.type, .largeOperator, desc)
XCTAssertTrue(op.limits); XCTAssertTrue(op.limits)
// convert it back to latex // convert it back to latex
latex = MTMathListBuilder.mathListToString(list) latex = MTMathListBuilder.mathListToString(list)