(_ function: @escaping (A) -> B) -> Parser {
77 | return ActionParser(self, function, true)
78 | }
79 |
80 | public func pick(_ index: Int) -> Parser {
81 | let function: ([Any]) -> Any = Functions.nthOfList(index)
82 | return map(function)
83 | }
84 |
85 | public func permute(_ indexes: Int...) -> Parser {
86 | return map(Functions.permutationOfList(indexes))
87 | }
88 |
89 | public func separatedBy(_ separator: Parser) -> Parser {
90 | let function: ([Any]) -> [Any] = Functions.separateByUnpack()
91 |
92 | return SequenceParser(self, SequenceParser(separator, self).star())
93 | .map(function)
94 | }
95 |
96 | public func delimitedBy(_ separator: Parser) -> Parser {
97 | let function: ([Any]) -> [Any] = Functions.delimitedByUnpack()
98 |
99 | return separatedBy(separator).seq(separator.optional())
100 | .map(function)
101 | }
102 |
103 | public func replace(_ source: Parser, _ target: Parser) {
104 | // no referring parsers
105 | }
106 |
107 | public func hasEqualProperties(_ other: Parser) -> Bool {
108 | return true
109 | }
110 |
111 | public func getChildren() -> [Parser] {
112 | return []
113 | }
114 |
115 | public func copy() -> Parser {
116 | return Parser()
117 | }
118 | }
119 |
120 | extension Parser {
121 | /**
122 | * Recursively tests for structural similarity of two parsers.
123 | *
124 | * The code can automatically deals with recursive parsers and parsers
125 | * that refer to other parsers. This code is supposed to be overridden by
126 | * parsers that add other state.
127 | */
128 | public func isEqualTo(_ other: Parser) -> Bool {
129 | var seen: Set = Set()
130 | return isEqualTo(other, &seen)
131 | }
132 |
133 | /**
134 | * Recursively tests for structural similarity of two parsers.
135 | */
136 | func isEqualTo(_ other: Parser, _ seen: inout Set) -> Bool {
137 | if seen.contains(self) {
138 | return true
139 | }
140 | seen.insert(self)
141 |
142 | return isSameClass(other)
143 | && hasEqualProperties(other) && hasEqualChildren(other, &seen)
144 | }
145 |
146 | func isSameClass(_ other: Parser) -> Bool {
147 | return object_getClassName(self) == object_getClassName(other)
148 | }
149 |
150 | /**
151 | * Compares the children of two parsers.
152 | *
153 | * Normally subclasses should not override this method, but instead {@link
154 | * #getChildren()}.
155 | */
156 | func hasEqualChildren(_ other: Parser, _ seen: inout Set) -> Bool {
157 | let selfChildren = getChildren()
158 | let otherChildren = other.getChildren()
159 |
160 | if selfChildren.count != otherChildren.count {
161 | return false
162 | }
163 |
164 | for i in 0 ..< selfChildren.count {
165 | if !selfChildren[i].isEqualTo(otherChildren[i], &seen) {
166 | return false
167 | }
168 | }
169 |
170 | return true
171 | }
172 |
173 | }
174 |
--------------------------------------------------------------------------------
/src/swift-petitparser/tools/ExpressionGroup.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ExpressionGroup.swift
3 | // swift-petitparser
4 | //
5 | // Created by Philipp Arndt on 2019-12-20.
6 | // Copyright © 2019 petitparser. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public struct ExpressionResult {
12 | let op: Any
13 | let action: (Any) -> Any
14 | }
15 |
16 | public class ExpressionGroup {
17 | let loopback: Parser
18 |
19 | var primitives: [Parser] = []
20 | var wrappers: [Parser] = []
21 | var prefix: [Parser] = []
22 | var postfix: [Parser] = []
23 | var right: [Parser] = []
24 | var left: [Parser] = []
25 |
26 | init(_ loopback: Parser) {
27 | self.loopback = loopback
28 | }
29 |
30 | func buildChoice(_ parsers: [Parser]) -> Parser {
31 | buildChoice(parsers, FailureParser("otherwise"))
32 | }
33 |
34 | func buildChoice(_ parsers: [Parser], _ otherwise: Parser) -> Parser {
35 | if parsers.isEmpty {
36 | return otherwise
37 | }
38 | else if parsers.count == 1 {
39 | return parsers[0]
40 | }
41 | else {
42 | return ChoiceParser(parsers)
43 | }
44 | }
45 |
46 | public func build(_ inner: Parser) -> Parser {
47 | return buildLeft(buildRight(buildPostfix(buildPrefix(buildWrapper(buildPrimitive(inner))))))
48 | }
49 | }
50 |
51 | extension ExpressionGroup {
52 | @discardableResult public func primitive(_ parser: Parser) -> ExpressionGroup {
53 | primitives.append(parser)
54 | return self
55 | }
56 |
57 | @discardableResult public func primitive(_ parser: Parser, _ action: @escaping (A) -> B) -> ExpressionGroup {
58 | primitives.append(parser.map(action))
59 | return self
60 | }
61 |
62 | func buildPrimitive(_ inner: Parser) -> Parser {
63 | return buildChoice(primitives, inner)
64 | }
65 | }
66 |
67 | extension ExpressionGroup {
68 | @discardableResult public func wrapper(_ left: Parser, _ right: Parser) -> ExpressionGroup {
69 | wrappers.append(SequenceParser(left, loopback, right))
70 | return self
71 | }
72 |
73 | @discardableResult public func wrapper(_ left: Parser, _ right: Parser, _ action: @escaping (A) -> B) -> ExpressionGroup {
74 | wrappers.append(SequenceParser(left, loopback, right).map(action))
75 | return self
76 | }
77 |
78 | func buildWrapper(_ inner: Parser) -> Parser {
79 | var choices = wrappers
80 | choices.append(inner)
81 | return buildChoice(choices, inner)
82 | }
83 | }
84 |
85 | extension ExpressionGroup {
86 | @discardableResult public func prefix(_ parser: Parser) -> ExpressionGroup {
87 | prefix.append(parser.map { ExpressionResult(op: $0, action: { $0 }) })
88 | return self
89 | }
90 |
91 | @discardableResult public func prefix(_ parser: Parser, _ action: @escaping (A) -> B) -> ExpressionGroup {
92 | prefix.append(parser.map { ExpressionResult(op: $0, action: { action($0 as! A) }) })
93 | return self
94 | }
95 |
96 | func buildPrefix(_ inner: Parser) -> Parser {
97 | if prefix.isEmpty {
98 | return inner
99 | }
100 | else {
101 | return SequenceParser(buildChoice(prefix).star(), inner)
102 | .map(mapPrefix)
103 | }
104 | }
105 |
106 | private func mapPrefix(_ tuple: [Any]) -> Any {
107 | var value = tuple[1]
108 | var tuples = tuple[0] as! [ExpressionResult]
109 | tuples.reverse()
110 | for result in tuples {
111 | value = result.action([result.op, value])
112 | }
113 | return value
114 | }
115 | }
116 |
117 | extension ExpressionGroup {
118 | @discardableResult public func postfix(_ parser: Parser) -> ExpressionGroup {
119 | postfix.append(parser.map { ExpressionResult(op: $0, action: { $0 }) })
120 | return self
121 | }
122 |
123 | @discardableResult public func postfix(_ parser: Parser, _ action: @escaping (A) -> B) -> ExpressionGroup {
124 | postfix.append(parser.map { ExpressionResult(op: $0, action: { action($0 as! A) }) })
125 | return self
126 | }
127 |
128 | func buildPostfix(_ inner: Parser) -> Parser {
129 | if postfix.isEmpty {
130 | return inner
131 | }
132 | else {
133 | return SequenceParser(inner, buildChoice(postfix).star())
134 | .map(mapPostfix)
135 | }
136 | }
137 |
138 | private func mapPostfix(_ tuple: [Any]) -> Any {
139 | var value = tuple[0]
140 | let tuples = tuple[1] as! [ExpressionResult]
141 | for result in tuples {
142 | value = result.action([value, result.op])
143 | }
144 | return value
145 | }
146 | }
147 |
148 | extension ExpressionGroup {
149 | @discardableResult public func right(_ parser: Parser) -> ExpressionGroup {
150 | right.append(parser.map { ExpressionResult(op: $0, action: { $0 }) })
151 | return self
152 | }
153 |
154 | @discardableResult public func right(_ parser: Parser, _ action: @escaping (A) -> B) -> ExpressionGroup {
155 | right.append(parser.map { ExpressionResult(op: $0, action: { action($0 as! A) }) })
156 | return self
157 | }
158 |
159 | func buildRight(_ inner: Parser) -> Parser {
160 | if right.isEmpty {
161 | return inner
162 | }
163 | else {
164 | return inner.separatedBy(buildChoice(right))
165 | .map(mapRight)
166 | }
167 | }
168 |
169 | private func mapRight(_ innerSequence: [Any]) -> Any {
170 | var result = innerSequence.last!
171 |
172 | for i in stride(from: innerSequence.count - 2, to: 0, by: -2) {
173 | let expressionResult = innerSequence[i] as! ExpressionResult
174 | result = expressionResult.action([innerSequence[i - 1], expressionResult.op, result])
175 | }
176 |
177 | return result
178 | }
179 | }
180 |
181 | extension ExpressionGroup {
182 | @discardableResult public func left(_ parser: Parser) -> ExpressionGroup {
183 | left.append(parser.map { ExpressionResult(op: $0, action: { $0 }) })
184 | return self
185 | }
186 |
187 | @discardableResult public func left(_ parser: Parser, _ action: @escaping (A) -> B) -> ExpressionGroup {
188 | left.append(parser.map { ExpressionResult(op: $0, action: { action($0 as! A) }) })
189 | return self
190 | }
191 |
192 | func buildLeft(_ inner: Parser) -> Parser {
193 | if left.isEmpty {
194 | return inner
195 | }
196 | else {
197 | return inner.separatedBy(buildChoice(left))
198 | .map(mapLeft)
199 | }
200 | }
201 |
202 | private func mapLeft(_ innerSequence: [Any]) -> Any {
203 | var result = innerSequence[0]
204 |
205 | for i in stride(from: 1, to: innerSequence.count, by: 2) {
206 | let expressionResult = innerSequence[i] as! ExpressionResult
207 | result = expressionResult.action([result, expressionResult.op, innerSequence[i + 1]])
208 | }
209 |
210 | return result
211 | }
212 | }
213 |
--------------------------------------------------------------------------------
/src/Tests/ExpressionBuilderTest.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ExpressionBuilderTest.swift
3 | // Tests
4 | //
5 | // Created by Philipp Arndt on 2019-12-24.
6 | // Copyright © 2019 petitparser. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import XCTest
11 | @testable import swift_petitparser
12 |
13 | class ExpressionBuilderTest: XCTestCase {
14 |
15 | var parser: Parser = CP.any()
16 | var evaluator: Parser = CP.any()
17 |
18 | override func setUp() {
19 | setUpParser()
20 | setUpEvaluator()
21 | }
22 |
23 | fileprivate func setUpParser() {
24 | let builder = ExpressionBuilder()
25 | builder.group()
26 | .primitive(CP.digit().plus().seq(CP.of(".")
27 | .seq(CP.digit().plus()).optional())
28 | .flatten()
29 | .trim())
30 | .wrapper(CP.of("(").trim(), CP.of(")").trim())
31 | builder.group()
32 | .prefix(CP.of("-").trim())
33 | builder.group()
34 | .postfix(SP.of("++").trim())
35 | .postfix(SP.of("--").trim())
36 | builder.group()
37 | .right(CP.of("^").trim())
38 | builder.group()
39 | .left(CP.of("*").trim())
40 | .left(CP.of("/").trim())
41 | builder.group()
42 | .left(CP.of("+").trim())
43 | .left(CP.of("-").trim())
44 | parser = builder.build().end()
45 | }
46 |
47 | fileprivate func setUpEvaluator() {
48 | let builder = ExpressionBuilder()
49 | builder.group()
50 | .primitive(NumbersParser.double())
51 | .wrapper(
52 | CP.of("(").trim(),
53 | CP.of(")").trim(), { (nums: [Any]) -> Any in nums[1] })
54 | builder.group()
55 | .prefix(CP.of("-").trim(), { (nums: [AnyObject]) -> Double in
56 | -(nums[1] as! Double)
57 | })
58 | builder.group()
59 | .postfix(SP.of("++").trim(), { (nums: [AnyObject]) -> Double in
60 | (nums[0] as! Double + 1)
61 | })
62 | .postfix(SP.of("--").trim(), { (nums: [AnyObject]) -> Double in
63 | (nums[0] as! Double - 1)
64 | })
65 | builder.group()
66 | .right(CP.of("^").trim(), { (nums: [AnyObject]) -> Double in
67 | pow((nums[0] as! Double), (nums[2] as! Double))
68 | })
69 | builder.group()
70 | .left(CP.of("*").trim(), { (nums: [AnyObject]) -> Double in
71 | (nums[0] as! Double) * (nums[2] as! Double)
72 | })
73 | .left(CP.of("/").trim(), { (nums: [AnyObject]) -> Double in
74 | (nums[0] as! Double) / (nums[2] as! Double)
75 | })
76 | builder.group()
77 | .left(CP.of("+").trim(), { (nums: [AnyObject]) -> Double in
78 | (nums[0] as! Double) + (nums[2] as! Double)
79 | })
80 | .left(CP.of("-").trim(), { (nums: [AnyObject]) -> Double in
81 | (nums[0] as! Double) - (nums[2] as! Double)
82 | })
83 | evaluator = builder.build().end()
84 | }
85 |
86 | func assertParse(_ input: String, _ expected: T) {
87 | let actual: T = parser.parse(input).get()!
88 | XCTAssertEqual(actual, expected)
89 | }
90 |
91 | func assertEvaluation(_ input: String, _ expected: T) {
92 | let actual: T = evaluator.parse(input).get()!
93 | XCTAssertEqual(actual, expected)
94 | }
95 |
96 | func testParseNumber() {
97 | assertParse("0", "0")
98 | assertParse("1.2", "1.2")
99 | assertParse("34.78", "34.78")
100 | }
101 |
102 | func testEvaluateNumber() {
103 | assertEvaluation("0", 0.0)
104 | assertEvaluation("0.0", 0.0)
105 | assertEvaluation("1", 1.0)
106 | assertEvaluation("1.2", 1.2)
107 | assertEvaluation("34", 34.0)
108 | assertEvaluation("34.7", 34.7)
109 | assertEvaluation("56.78", 56.78)
110 | }
111 | //
112 | // func testParseNegativeNumber() {
113 | // assertParse("-1", ["-", "1"])
114 | // assertParse("-1.2", ["-", "1.2"])
115 | // }
116 |
117 | func testEvaluateAdd() {
118 | assertEvaluation("1 + 2", 3.0)
119 | assertEvaluation("2 + 1", 3.0)
120 | assertEvaluation("1 + 2.3", 3.3)
121 | assertEvaluation("2.3 + 1", 3.3)
122 | assertEvaluation("1 + -2", -1.0)
123 | assertEvaluation("-2 + 1", -1.0)
124 | }
125 |
126 | func testEvaluateAddMany() {
127 | assertEvaluation("1", 1.0)
128 | assertEvaluation("1 + 2", 3.0)
129 | assertEvaluation("1 + 2 + 3", 6.0)
130 | assertEvaluation("1 + 2 + 3 + 4", 10.0)
131 | assertEvaluation("1 + 2 + 3 + 4 + 5", 15.0)
132 | }
133 |
134 | func testEvaluateSub() {
135 | assertEvaluation("1 - 2", -1.0)
136 | assertEvaluation("1.2 - 1.2", 0.0)
137 | assertEvaluation("1 - -2", 3.0)
138 | assertEvaluation("-1 - -2", 1.0)
139 | }
140 |
141 | func testEvaluateSubMany() {
142 | assertEvaluation("1", 1.0)
143 | assertEvaluation("1 - 2", -1.0)
144 | assertEvaluation("1 - 2 - 3", -4.0)
145 | assertEvaluation("1 - 2 - 3 - 4", -8.0)
146 | assertEvaluation("1 - 2 - 3 - 4 - 5", -13.0)
147 | }
148 |
149 | func testEvaluateMul() {
150 | assertEvaluation("2 * 3", 6.0)
151 | assertEvaluation("2 * -4", -8.0)
152 | }
153 |
154 | func testEvaluateMulMany() {
155 | assertEvaluation("1 * 2", 2.0)
156 | assertEvaluation("1 * 2 * 3", 6.0)
157 | assertEvaluation("1 * 2 * 3 * 4", 24.0)
158 | assertEvaluation("1 * 2 * 3 * 4 * 5", 120.0)
159 | }
160 |
161 | func testEvaluateDiv() {
162 | assertEvaluation("12 / 3", 4.0)
163 | assertEvaluation("-16 / -4", 4.0)
164 | }
165 |
166 | func testEvaluateDivMany() {
167 | assertEvaluation("100 / 2", 50.0)
168 | assertEvaluation("100 / 2 / 2", 25.0)
169 | assertEvaluation("100 / 2 / 2 / 5", 5.0)
170 | assertEvaluation("100 / 2 / 2 / 5 / 5", 1.0)
171 | }
172 |
173 | func testEvaluatePow() {
174 | assertEvaluation("2 ^ 3", 8.0)
175 | assertEvaluation("-2 ^ 3", -8.0)
176 | assertEvaluation("-2 ^ -3", -0.125)
177 | }
178 |
179 | func testEvaluatePowMany() {
180 | assertEvaluation("4 ^ 3", 64.0)
181 | assertEvaluation("4 ^ 3 ^ 2", 262144.0)
182 | assertEvaluation("4 ^ 3 ^ 2 ^ 1", 262144.0)
183 | assertEvaluation("4 ^ 3 ^ 2 ^ 1 ^ 0", 262144.0)
184 | }
185 |
186 | func testEvaluateParenthesis() {
187 | assertEvaluation("(1)", 1.0)
188 | assertEvaluation("(1 + 2)", 3.0)
189 | assertEvaluation("((1))", 1.0)
190 | assertEvaluation("((1 + 2))", 3.0)
191 | assertEvaluation("2 * (3 + 4)", 14.0)
192 | assertEvaluation("(2 + 3) * 4", 20.0)
193 | assertEvaluation("6 / (2 + 4)", 1.0)
194 | assertEvaluation("(2 + 6) / 2", 4.0)
195 | }
196 |
197 | func testEvaluatePriority() {
198 | assertEvaluation("2 * 3 + 4", 10.0)
199 | assertEvaluation("2 + 3 * 4", 14.0)
200 | assertEvaluation("6 / 3 + 4", 6.0)
201 | assertEvaluation("2 + 6 / 2", 5.0)
202 | }
203 |
204 | func testEvaluatePostfixAdd() {
205 | assertEvaluation("0++", 1.0)
206 | assertEvaluation("0++++", 2.0)
207 | assertEvaluation("0++++++", 3.0)
208 | assertEvaluation("0+++1", 2.0)
209 | assertEvaluation("0+++++1", 3.0)
210 | assertEvaluation("0+++++++1", 4.0)
211 | }
212 |
213 | func testEvaluatePostfixSub() {
214 | assertEvaluation("1--", 0.0)
215 | assertEvaluation("2----", 0.0)
216 | assertEvaluation("3------", 0.0)
217 | assertEvaluation("2---1", 0.0)
218 | assertEvaluation("3-----1", 0.0)
219 | assertEvaluation("4-------1", 0.0)
220 | }
221 |
222 | func testEvaluatePrefixNegate() {
223 | assertEvaluation("1", 1.0)
224 | assertEvaluation("-1", -1.0)
225 | assertEvaluation("--1", 1.0)
226 | assertEvaluation("---1", -1.0)
227 | }
228 | }
229 |
--------------------------------------------------------------------------------
/src/Tests/ExamplesTest.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ExamplesTest.swift
3 | // Tests
4 | //
5 | // Created by Philipp Arndt on 2019-12-20.
6 | // Copyright © 2019 petitparser. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import XCTest
11 | @testable import swift_petitparser
12 |
13 | class ExampleTypes {
14 | static let identifier = (CP.letter() + CP.word()<*>).flatten()
15 |
16 | static let FRACTION = CP.of(".") + CP.digit()<+>
17 |
18 | static let NUMBER = (CP.of("-").optional()
19 | + CP.digit()<+> + FRACTION.optional())
20 | .flatten()
21 |
22 | static let STRING = (CP.of("\"")
23 | + CP.any().starLazy(CP.of("\""))
24 | + CP.of("\""))
25 | .flatten()
26 |
27 | static let RETURN = (SP.of("return")
28 | + CP.whitespace()<+>.flatten()
29 | + (identifier | NUMBER | STRING))
30 | .pick(-1)
31 |
32 | static let JAVADOC = (SP.of("/**")
33 | + CP.any().starLazy(SP.of("*/"))
34 | + SP.of("*/"))
35 | .flatten()
36 |
37 | static let DOUBLE = (CP.digit()<+> + FRACTION.optional())
38 | .flatten().trim()
39 | .map { (d: String) -> Double in Double(d)! }
40 | }
41 |
42 | // swiftlint:disable type_name
43 | typealias T = ExampleTypes
44 |
45 | class ExamplesTests: XCTestCase {
46 |
47 | func testIdentifierSuccess() {
48 | Assert.assertSuccess(T.identifier, "a", "a")
49 | Assert.assertSuccess(T.identifier, "a1", "a1")
50 | Assert.assertSuccess(T.identifier, "a12", "a12")
51 | Assert.assertSuccess(T.identifier, "ab", "ab")
52 | Assert.assertSuccess(T.identifier, "a1b", "a1b")
53 | }
54 |
55 | func testIdentifierIncomplete() {
56 | Assert.assertSuccess(T.identifier, "a_", "a", 1)
57 | Assert.assertSuccess(T.identifier, "a1-", "a1", 2)
58 | Assert.assertSuccess(T.identifier, "a12+", "a12", 3)
59 | Assert.assertSuccess(T.identifier, "ab ", "ab", 2)
60 | }
61 |
62 | func testIdentifierFailure() {
63 | Assert.assertFailure(T.identifier, "", "letter expected")
64 | Assert.assertFailure(T.identifier, "1", "letter expected")
65 | Assert.assertFailure(T.identifier, "1a", "letter expected")
66 | }
67 |
68 | func testNumberPositiveSuccess() {
69 | Assert.assertSuccess(T.NUMBER, "1", "1")
70 | Assert.assertSuccess(T.NUMBER, "12", "12")
71 | Assert.assertSuccess(T.NUMBER, "12.3", "12.3")
72 | Assert.assertSuccess(T.NUMBER, "12.34", "12.34")
73 | }
74 |
75 | func testNumberNegativeSuccess() {
76 | Assert.assertSuccess(T.NUMBER, "-1", "-1")
77 | Assert.assertSuccess(T.NUMBER, "-12", "-12")
78 | Assert.assertSuccess(T.NUMBER, "-12.3", "-12.3")
79 | Assert.assertSuccess(T.NUMBER, "-12.34", "-12.34")
80 | }
81 |
82 | func testNumberIncomplete() {
83 | Assert.assertSuccess(T.NUMBER, "1..", "1", 1)
84 | Assert.assertSuccess(T.NUMBER, "12-", "12", 2)
85 | Assert.assertSuccess(T.NUMBER, "12.3.", "12.3", 4)
86 | Assert.assertSuccess(T.NUMBER, "12.34.", "12.34", 5)
87 | }
88 |
89 | func testNumberFailure() {
90 | Assert.assertFailure(T.NUMBER, "", "digit expected")
91 | Assert.assertFailure(T.NUMBER, "-", 1, "digit expected")
92 | Assert.assertFailure(T.NUMBER, "-x", 1, "digit expected")
93 | Assert.assertFailure(T.NUMBER, ".", "digit expected")
94 | Assert.assertFailure(T.NUMBER, ".1", "digit expected")
95 | }
96 |
97 | func testStringSuccess() {
98 | Assert.assertSuccess(T.STRING, "\"\"", "\"\"")
99 | Assert.assertSuccess(T.STRING, "\"a\"", "\"a\"")
100 | Assert.assertSuccess(T.STRING, "\"ab\"", "\"ab\"")
101 | Assert.assertSuccess(T.STRING, "\"abc\"", "\"abc\"")
102 | }
103 |
104 | func testStringIncomplete() {
105 | Assert.assertSuccess(T.STRING, "\"\"x", "\"\"", 2)
106 | Assert.assertSuccess(T.STRING, "\"a\"x", "\"a\"", 3)
107 | Assert.assertSuccess(T.STRING, "\"ab\"x", "\"ab\"", 4)
108 | Assert.assertSuccess(T.STRING, "\"abc\"x", "\"abc\"", 5)
109 | }
110 |
111 | func testStringFailure() {
112 | Assert.assertFailure(T.STRING, "\"", 1, "'\"' expected")
113 | Assert.assertFailure(T.STRING, "\"a", 2, "'\"' expected")
114 | Assert.assertFailure(T.STRING, "\"ab", 3, "'\"' expected")
115 | Assert.assertFailure(T.STRING, "a\"", "'\"' expected")
116 | Assert.assertFailure(T.STRING, "ab\"", "'\"' expected")
117 | }
118 |
119 | func testReturnSuccess() {
120 | Assert.assertSuccess(T.RETURN, "return f", "f")
121 | Assert.assertSuccess(T.RETURN, "return f", "f")
122 | Assert.assertSuccess(T.RETURN, "return foo", "foo")
123 | Assert.assertSuccess(T.RETURN, "return foo", "foo")
124 | Assert.assertSuccess(T.RETURN, "return 1", "1")
125 | Assert.assertSuccess(T.RETURN, "return 1", "1")
126 | Assert.assertSuccess(T.RETURN, "return -2.3", "-2.3")
127 | Assert.assertSuccess(T.RETURN, "return -2.3", "-2.3")
128 | Assert.assertSuccess(T.RETURN, "return \"a\"", "\"a\"")
129 | Assert.assertSuccess(T.RETURN, "return \"a\"", "\"a\"")
130 | }
131 |
132 | func testReturnFailure() {
133 | Assert.assertFailure(T.RETURN, "retur f", 0, "return expected")
134 | Assert.assertFailure(T.RETURN, "return1", 6, "whitespace expected")
135 | Assert.assertFailure(T.RETURN, "return $", 8, "'\"' expected")
136 | }
137 |
138 | func testJavaDoc() {
139 | Assert.assertSuccess(T.JAVADOC, "/** foo */", "/** foo */")
140 | Assert.assertSuccess(T.JAVADOC, "/** * * */", "/** * * */")
141 | }
142 |
143 | func testExpression() {
144 | let term = SettableParser.undefined()
145 | let prod = SettableParser.undefined()
146 | let prim = SettableParser.undefined()
147 |
148 | term.set(prod.seq(CP.of("+").trim()).seq(term)
149 | .map { (nums: [Any]) -> Int in (nums[0] as! Int) + (nums[2] as! Int) }
150 | .or(prod))
151 |
152 | prod.set(prim.seq(CP.of("*").trim()).seq(prod)
153 | .map { (nums: [Any]) -> Int in (nums[0] as! Int) * (nums[2] as! Int) }
154 | .or(prim))
155 |
156 | prim.set((CP.of("(").trim().seq(term).seq(CP.of(")").trim()))
157 | .map { (nums: [Any]) -> Int in nums[1] as! Int }
158 | .or(NumbersParser.int()))
159 |
160 | let start = term.end()
161 | Assert.assertSuccess(start, "1 + 1", 2)
162 | Assert.assertSuccess(start, "1 + 2 * 3", 7)
163 | Assert.assertSuccess(start, "(1 + 2) * 3", 9)
164 | }
165 |
166 | func testExpressionBuilderWithSettableExample() {
167 | let recursion = SettableParser.undefined()
168 |
169 | let bracket = CP.of("(")
170 | .seq(recursion)
171 | .seq(CP.of(")"))
172 | .map { (nums: [Any]) -> Double in nums[1] as! Double }
173 |
174 | let builder = ExpressionBuilder()
175 | builder.group()
176 | .primitive(bracket.or(T.DOUBLE))
177 |
178 | initOps(builder)
179 |
180 | recursion.set(builder.build())
181 | let parser = recursion.end()
182 | assertCalculatorExample(parser)
183 | }
184 |
185 | func testExpressionBuilderWithWrapperExample() {
186 | let builder = ExpressionBuilder()
187 | builder.group()
188 | .primitive(NumbersParser.double())
189 | .wrapper(CP.of("(").trim(), CP.of(")").trim(), { (nums: [Any]) -> Any in nums[1] })
190 |
191 | initOps(builder)
192 |
193 | let parser = builder.build().end()
194 | assertCalculatorExample(parser)
195 | }
196 |
197 | private func initOps(_ builder: ExpressionBuilder) {
198 | // negation is a prefix operator
199 | builder.group()
200 | .prefix(CP.of("-").trim(), { (nums: [Any]) -> Double in
201 | -(nums[1] as! Double)
202 | })
203 |
204 | // power is right-associative
205 | builder.group()
206 | .right(CP.of("^").trim(), { (nums: [Any]) -> Double in
207 | pow((nums[0] as! Double), (nums[2] as! Double))
208 | })
209 |
210 | // multiplication and addition are left-associative
211 | builder.group()
212 | .left(CP.of("*").trim(), { (nums: [Any]) -> Double in
213 | (nums[0] as! Double) * (nums[2] as! Double)
214 | })
215 | .left(CP.of("/").trim(), { (nums: [Any]) -> Double in
216 | (nums[0] as! Double) / (nums[2] as! Double)
217 | })
218 |
219 | builder.group()
220 | .left(CP.of("+").trim(), { (nums: [Any]) -> Double in
221 | (nums[0] as! Double) + (nums[2] as! Double)
222 | })
223 | .left(CP.of("-").trim(), { (nums: [Any]) -> Double in
224 | (nums[0] as! Double) - (nums[2] as! Double)
225 | })
226 | }
227 |
228 | func assertCalculatorExample(_ parser: Parser) {
229 | let intCalculator = parser.map { (value: Double) -> Int in
230 | Int(value)
231 | }
232 |
233 | Assert.assertSuccess(intCalculator, "9 - 4 - 2", 3)
234 | Assert.assertSuccess(intCalculator, "9 - (4 - 2)", 7)
235 | Assert.assertSuccess(intCalculator, "-8", -8)
236 | Assert.assertSuccess(intCalculator, "1+2*3", 7)
237 | Assert.assertSuccess(intCalculator, "1*2+3", 5)
238 | Assert.assertSuccess(intCalculator, "8/4/2", 1)
239 | Assert.assertSuccess(intCalculator, "2^2^3", 256)
240 | }
241 |
242 | func testIPAddress() {
243 | let ip = (NumbersParser.int(from: 0, to: 255)
244 | + (CP.of(".") + NumbersParser.int(from: 0, to: 255)).times(3))
245 | .flatten().trim()
246 |
247 | Assert.assertSuccess(ip, "10.0.0.1", "10.0.0.1")
248 | Assert.assertSuccess(ip, " 10.0.0.1", "10.0.0.1")
249 | }
250 |
251 | func testHostName() {
252 | let host = (CP.word() | CP.of("."))<+>.flatten().trim()
253 |
254 | Assert.assertSuccess(host, " some.example.com ", "some.example.com")
255 | }
256 |
257 | func testIPAddressOrHostName() {
258 | let ip = (NumbersParser.int(from: 0, to: 255)
259 | + (CP.of(".") + NumbersParser.int(from: 0, to: 255)).times(3))
260 |
261 | let host = (CP.word() | CP.of("."))<+>
262 | let parser = (ip | host).flatten().trim()
263 | Assert.assertSuccess(parser, "10.0.0.1", "10.0.0.1")
264 | Assert.assertSuccess(parser, " 10.0.0.1", "10.0.0.1")
265 | Assert.assertSuccess(parser, " some.example.com ", "some.example.com")
266 | }
267 |
268 | func testHostname() {
269 | XCTAssertEqual(validateHostname(name: "pisvr"), "pisvr")
270 | }
271 |
272 | func testHostnameNonAscii() {
273 | XCTAssertNil(validateHostname(name: "pisvr💖"))
274 | }
275 |
276 | private func validateHostname(name hostname: String) -> String? {
277 | let ip = NumbersParser.int(from: 0, to: 255)
278 | .seq(CharacterParser.of(".").seq(NumbersParser.int(from: 0, to: 255)).times(3))
279 |
280 | let host = CharacterParser.pattern("a-zA-Z0-9./-").plus()
281 | let parser = ip.or(host).flatten().trim().end()
282 | return parser.parse(hostname).get()
283 | }
284 |
285 | }
286 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | PetitParser for Swift
2 | =====================
3 |
4 | [](LICENSE)
5 |
6 | Grammars for programming languages are traditionally specified statically. They are hard to compose and reuse due to ambiguities that inevitably arise. PetitParser combines ideas from scannnerless parsing, parser combinators, parsing expression grammars and packrat parsers to model grammars and parsers as objects that can be reconfigured dynamically.
7 |
8 | This library is based on [java-petitparser](https://github.com/petitparser/java-petitparser) from Lukas Renggli.
9 | It has been adapted to Swift mainly as coding kata.
10 |
11 |
12 | Installation
13 | ------------
14 |
15 | ### Swift package
16 |
17 | Use `https://github.com/philipparndt/swift-petitparser.git` as Swift package
18 |
19 | ### CocoaPod
20 |
21 | ```
22 | target 'MyApp' do
23 | pod 'swift-petitparser', '~> 1.0'
24 | end
25 | ```
26 |
27 | Tutorial
28 | --------
29 |
30 | ### Writing a Simple Grammar
31 |
32 | Writing grammars with PetitParser is simple as writing Swift code. For example, to write a grammar that can parse identifiers that start with a letter followed by zero or more letter or digits is defined as follows:
33 |
34 | We use the following import and type alias for all examples:
35 | ```swift
36 | import swift_petitparser
37 | typealias CP = CharacterParser
38 | typealias SP = StringParser
39 | ```
40 |
41 | ```swift
42 | class Example {
43 | init() {
44 | let id = CP.letter().seq(CP.letter().or(CP.digit()).star())
45 | ...
46 | }
47 | }
48 | ```
49 |
50 | Or using operator overloads:
51 | ```swift
52 | class Example {
53 | init() {
54 | let id = CP.letter() + (CP.letter() | CP.digit())<*>
55 | ...
56 | }
57 | }
58 | ```
59 |
60 | If you look at the object `id` in the debugger, you'll notice that the code above builds a tree of parser objects:
61 |
62 | - `SequenceParser`: This parser accepts a sequence of parsers.
63 | - `CharacterParser`: This parser accepts a single letter.
64 | - `PossessiveRepeatingParser`: This parser accepts zero or more times another parser.
65 | - `ChoiceParser`: This parser accepts a single word character.
66 | - `CharacterParser`: This parser accepts a single letter.
67 | - `CharacterParser`: This parser accepts a single digit.
68 |
69 | ### Parsing Some Input
70 |
71 | To actually parse a `String` we can use the method `Parser#parse(String)`:
72 |
73 | ```swift
74 | let id1 = id.parse("yeah")
75 | let id2 = id.parse("f12")
76 | ```
77 |
78 | The method `String` returns `Result`, which is either an instance of `Success` or `Failure`. In both examples above we are successful and can retrieve the parse result using `Success#get()`:
79 |
80 | ```swift
81 | print(id1.get()!) // ["y", ["e", "a", "h"]]
82 | print(id2.get()!) // ["f", ["1", "2"]]
83 | ```
84 |
85 | While it seems odd to get these nested arrays with characters as a return value, this is the default decomposition of the input into a parse tree. We'll see in a while how that can be customized.
86 |
87 | If we try to parse something invalid we get an instance of `Failure` as an answer and we can retrieve a descriptive error message using `Failure#getMessage()`:
88 |
89 | ```swift
90 | let text = "123"
91 | let id3 = id.parse(text)
92 | print(id3.message!) // "letter expected"
93 | print(id3.position.utf16Offset(in: text)) // 0
94 | ```
95 |
96 | Trying to retrieve the parse result by calling `Failure#get()` will return an `nil` optional. `Result#isSuccess()` and `Result#isFailure()` can be used to decide if the parse was successful.
97 |
98 | If you are only interested if a given string matches or not you can use the helper method `Parser#accept(String)`:
99 |
100 | ```swift
101 | print(id.accept("foo")) // true
102 | print(id.accept("123")) // false
103 | ```
104 |
105 | ### Different Kinds of Parsers
106 |
107 | PetitParser provide a large set of ready-made parser that you can compose to consume and transform arbitrarily complex languages. The terminal parsers are the most simple ones. We've already seen a few of those:
108 |
109 | - `CharacterParser.of("a")` parses the character _a_.
110 | - `StringParser.of("abc")` parses the string _abc_.
111 | - `CharacterParser.any()` parses any character.
112 | - `CharacterParser.digit()` parses any digit from _0_ to _9_.
113 | - `CharacterParser.letter()` parses any letter from _a_ to _z_ and _A_ to _Z_.
114 | - `CharacterParser.word()` parses any letter or digit.
115 |
116 | Many other parsers are available in `CharacterParser` and `StringParser`.
117 |
118 | So instead of using the letter and digit predicate, we could have written our identifier parser like this:
119 |
120 | ```swift
121 | let id = CP.letter().seq(CP.word().star())
122 | ```
123 |
124 | or even:
125 | ```swift
126 | let id = CP.letter() + CP.word()<*>
127 | ```
128 |
129 | The next set of parsers are used to combine other parsers together:
130 |
131 | - `p1.seq(p2)` parses `p1` followed by `p2` (sequence).
132 | - `p1.or(p2)` parses `p1`, if that doesn't work parses `p2` (ordered choice).
133 | - `p.star()` parses `p` zero or more times.
134 | - `p.plus()` parses `p` one or more times.
135 | - `p.optional()` parses `p`, if possible.
136 | - `p.and()` parses `p`, but does not consume its input.
137 | - `p.not()` parses `p` and succeed when p fails, but does not consume its input.
138 | - `p.end()` parses `p` and succeed at the end of the input.
139 |
140 | To attach an action or transformation to a parser we can use the following methods:
141 |
142 | - `p.map { somthing_with_$0 }` performs the transformation given the function.
143 | - `p.pick(n)` returns the `n`-th element of the list `p` returns.
144 | - `p.flatten()` creates a string from the result of `p`.
145 | - `p.token()` creates a token from the result of `p`.
146 | - `p.trim()` trims whitespaces before and after `p`.
147 |
148 | To return a string of the parsed identifier, we can modify our parser like this:
149 |
150 | ```swift
151 | let id_b = CP.letter().seq(CP.word().star()).flatten()
152 | print(id_b.parse("yeah").get()!) // yeah
153 | ```
154 |
155 | or:
156 |
157 | ```swift
158 | let id_b = (CP.letter() + CP.word()<*>).flatten()
159 | print(id_b.parse("yeah").get()!) // yeah
160 | ```
161 |
162 | To conveniently find all matches in a given input string you can use `Parser#matchesSkipping(String)`:
163 |
164 | ```swift
165 | let id = CP.letter().seq(CP.word().star()).flatten()
166 | let matches: [String] = id.matchesSkipping("foo 123 bar4")
167 | print(matches) // ["foo", "bar4"]
168 | ```
169 |
170 | These are the basic elements to build parsers. There are a few more well documented and tested factory methods in the `Parser` class. If you want, browse their documentation and tests.
171 |
172 | ### Writing a More Complicated Grammar
173 |
174 | Now we are able to write a more complicated grammar for evaluating simple arithmetic expressions. Within a file we start with the grammar for a number (actually an integer):
175 |
176 | ```swift
177 | let number = CP.digit().plus().flatten().trim()
178 | .map { (d: String) -> Int in Int(d)! }
179 |
180 | // let number = CP.digit()<+>.flatten().trim()
181 | // .map { (d: String) -> Int in Int(d)! }
182 |
183 | print(number.parse("123").get()!) // 123
184 | ```
185 |
186 | Then we define the productions for addition and multiplication in order of precedence. Note that we instantiate the productions with undefined parsers upfront, because they recursively refer to each other. Later on we can resolve this recursion by setting their reference:
187 |
188 | ```swift
189 | let term = SettableParser.undefined()
190 | let prod = SettableParser.undefined()
191 | let prim = SettableParser.undefined()
192 |
193 | term.set(prod.seq(CP.of("+").trim()).seq(term)
194 | .map { (nums: [Any]) -> Int in (nums[0] as! Int) + (nums[2] as! Int) }
195 | .or(prod))
196 |
197 | prod.set(prim.seq(CP.of("*").trim()).seq(prod)
198 | .map { (nums: [Any]) -> Int in (nums[0] as! Int) * (nums[2] as! Int) }
199 | .or(prim))
200 |
201 | prim.set((CP.of("(").trim().seq(term).seq(CP.of(")").trim()))
202 | .map { (nums: [Any]) -> Int in nums[1] as! Int }
203 | .or(NumbersParser.int()))
204 | ```
205 |
206 | To make sure that our parser consumes all input we wrap it with the `end()` parser into the start production:
207 |
208 | ```swift
209 | let start = term.end()
210 | ```
211 |
212 | That's it, now we can test our parser and evaluator:
213 |
214 | ```swift
215 | print(start.parse("1 + 2 * 3").get()!) // 7
216 | print(start.parse("(1 + 2) * 3").get()!) // 9
217 | ```
218 |
219 | As an exercise we could extend the parser to also accept negative numbers and floating point numbers, not only integers. Furthermore it would be useful to support subtraction and division as well. All these features
220 | can be added with a few lines of PetitParser code.
221 |
222 | ### Using the Expression Builder
223 |
224 | Writing such expression parsers is pretty common and can be quite tricky to get right. To simplify things, PetitParser comes with a builder that can help you to define such grammars easily. It supports the definition of operator precedence; and prefix, postfix, left- and right-associative operators.
225 |
226 | The following code creates the empty expression builder:
227 |
228 | ```swift
229 | let builder = ExpressionBuilder()
230 | ```
231 |
232 | Then we define the operator-groups in descending precedence. The highest precedence are the literal numbers themselves. This time we accept floating point numbers, not just integers. In the same group we add support for parenthesis:
233 |
234 | ```swift
235 | builder.group()
236 | .primitive(NumbersParser.double())
237 | .wrapper(CP.of("(").trim(), CP.of(")").trim(), { (nums: [Any]) -> Any in nums[1] })
238 | ```
239 |
240 | Then come the normal arithmetic operators. Note, that the action blocks receive both, the terms and the parsed operator in the order they appear in the parsed input:
241 |
242 | ```swift
243 | // negation is a prefix operator
244 | builder.group()
245 | .prefix(CP.of("-").trim(), { (nums: [Any]) -> Double in
246 | -(nums[1] as! Double)
247 | })
248 |
249 | // power is right-associative
250 | builder.group()
251 | .right(CP.of("^").trim(), { (nums: [Any]) -> Double in
252 | pow((nums[0] as! Double), (nums[2] as! Double))
253 | })
254 |
255 | // multiplication and addition are left-associative
256 | builder.group()
257 | .left(CP.of("*").trim(), { (nums: [Any]) -> Double in
258 | (nums[0] as! Double) * (nums[2] as! Double)
259 | })
260 | .left(CP.of("/").trim(), { (nums: [Any]) -> Double in
261 | (nums[0] as! Double) / (nums[2] as! Double)
262 | })
263 |
264 | builder.group()
265 | .left(CP.of("+").trim(), { (nums: [Any]) -> Double in
266 | (nums[0] as! Double) + (nums[2] as! Double)
267 | })
268 | .left(CP.of("-").trim(), { (nums: [Any]) -> Double in
269 | (nums[0] as! Double) - (nums[2] as! Double)
270 | })
271 | ```
272 |
273 | Finally we can build the parser:
274 |
275 | ```swift
276 | let parser = builder.build().end()
277 | ```
278 |
279 | After executing the above code we get an efficient parser that correctly
280 | evaluates expressions like:
281 |
282 | ```java
283 | parser.parse("-8").get()! // -8
284 | parser.parse("1+2*3").get()! // 7
285 | parser.parse("1*2+3").get()! // 5
286 | parser.parse("8/4/2").get()! // 1
287 | parser.parse("2^2^3").get()! // 256
288 | ```
289 |
290 | You can find this example as test case here: [ExamplesTest.java](src/Tests/ExamplesTest.swift)
291 |
292 | Misc
293 | ----
294 |
295 | ### History
296 |
297 | PetitParser was originally implemented by Lukas Renggli in
298 | - [Smalltalk](http://scg.unibe.ch/research/helvetia/petitparser).
299 | - [Java](https://github.com/petitparser/java-petitparser) and
300 | - [Dart](https://github.com/petitparser/dart-petitparser).
301 |
302 | and adapted to Swift by Philipp Arndt
303 | - [Swift](https://github.com/philipparndt/swift-petitparser)
304 |
305 | The implementations are very similar in their API and the supported features. If possible, the implementations adopt best practises of the target language.
306 |
307 | ### Implementations
308 |
309 | - [Dart](https://github.com/petitparser/dart-petitparser)
310 | - [Java](https://github.com/petitparser/java-petitparser)
311 | - [PHP](https://github.com/mindplay-dk/petitparserphp)
312 | - [Smalltalk](http://scg.unibe.ch/research/helvetia/petitparser)
313 | - [Swift](https://github.com/philipparndt/swift-petitparser)
314 | - [TypeScript](https://github.com/mindplay-dk/petitparser-ts)
315 |
316 | ### License
317 |
318 | The MIT License, see [LICENSE](LICENSE).
319 |
--------------------------------------------------------------------------------
/src/Tests/CharacterTest.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CharacterTest.swift
3 | // Tests
4 | //
5 | // Created by Philipp Arndt on 2019-12-19.
6 | // Copyright © 2019 petitparser. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | import XCTest
12 | @testable import swift_petitparser
13 |
14 | class CharacterTest: XCTestCase {
15 | let _0: Character = "0"
16 | let _1: Character = "1"
17 | let _9: Character = "9"
18 | let minus: Character = "-"
19 | let space: Character = " "
20 | let a: Character = "a"
21 | let b: Character = "b"
22 | let c: Character = "c"
23 | let d: Character = "d"
24 | let e: Character = "e"
25 | let f: Character = "f"
26 | let g: Character = "g"
27 | let h: Character = "h"
28 | let i: Character = "i"
29 | let o: Character = "o"
30 | let p: Character = "p"
31 | let r: Character = "r"
32 | let t: Character = "t"
33 | let x: Character = "x"
34 | let X: Character = "X"
35 | let y: Character = "y"
36 |
37 | func testAny() {
38 | let parser = CP.any()
39 | Assert.assertSuccess(parser, "a", a)
40 | Assert.assertSuccess(parser, "b", b)
41 | Assert.assertFailure(parser, "", "any character expected")
42 | }
43 |
44 | func testAnyWithMessage() {
45 | let parser = CP.any("wrong")
46 | Assert.assertSuccess(parser, "a", a)
47 | Assert.assertSuccess(parser, "b", b)
48 | Assert.assertFailure(parser, "", "wrong")
49 | }
50 |
51 | func testAnyOf() {
52 | let parser = CP.anyOf("uncopyrightable")
53 | Assert.assertSuccess(parser, "c", c)
54 | Assert.assertSuccess(parser, "g", g)
55 | Assert.assertSuccess(parser, "h", h)
56 | Assert.assertSuccess(parser, "i", i)
57 | Assert.assertSuccess(parser, "o", o)
58 | Assert.assertSuccess(parser, "p", p)
59 | Assert.assertSuccess(parser, "r", r)
60 | Assert.assertSuccess(parser, "t", t)
61 | Assert.assertSuccess(parser, "y", y)
62 | Assert.assertFailure(parser, "x", "any of 'uncopyrightable' expected")
63 | }
64 |
65 | func testAnyOfWithMessage() {
66 | let parser = CP.anyOf("uncopyrightable", "wrong")
67 | Assert.assertSuccess(parser, "c", c)
68 | Assert.assertSuccess(parser, "g", g)
69 | Assert.assertSuccess(parser, "h", h)
70 | Assert.assertSuccess(parser, "i", i)
71 | Assert.assertSuccess(parser, "o", o)
72 | Assert.assertSuccess(parser, "p", p)
73 | Assert.assertSuccess(parser, "r", r)
74 | Assert.assertSuccess(parser, "t", t)
75 | Assert.assertSuccess(parser, "y", y)
76 | Assert.assertFailure(parser, "x", "wrong")
77 | }
78 |
79 | func testAnyOfEmpty() {
80 | let parser = CP.anyOf("")
81 | Assert.assertFailure(parser, "a", "any of '' expected")
82 | Assert.assertFailure(parser, "b", "any of '' expected")
83 | Assert.assertFailure(parser, "", "any of '' expected")
84 | }
85 |
86 | func testNone() {
87 | let parser = CP.none()
88 | Assert.assertFailure(parser, "a", "no character expected")
89 | Assert.assertFailure(parser, "b", "no character expected")
90 | Assert.assertFailure(parser, "", "no character expected")
91 | }
92 |
93 | func testNoneWithMessage() {
94 | let parser = CP.none("wrong")
95 | Assert.assertFailure(parser, "a", "wrong")
96 | Assert.assertFailure(parser, "b", "wrong")
97 | Assert.assertFailure(parser, "", "wrong")
98 | }
99 |
100 | func testNoneOf() {
101 | let parser = CP.noneOf("uncopyrightable")
102 | Assert.assertSuccess(parser, "x", x)
103 | Assert.assertFailure(parser, "c", "none of 'uncopyrightable' expected")
104 | Assert.assertFailure(parser, "g", "none of 'uncopyrightable' expected")
105 | Assert.assertFailure(parser, "h", "none of 'uncopyrightable' expected")
106 | Assert.assertFailure(parser, "i", "none of 'uncopyrightable' expected")
107 | Assert.assertFailure(parser, "o", "none of 'uncopyrightable' expected")
108 | Assert.assertFailure(parser, "p", "none of 'uncopyrightable' expected")
109 | Assert.assertFailure(parser, "r", "none of 'uncopyrightable' expected")
110 | Assert.assertFailure(parser, "t", "none of 'uncopyrightable' expected")
111 | Assert.assertFailure(parser, "y", "none of 'uncopyrightable' expected")
112 | }
113 |
114 | func testNoneOfWithMessage() {
115 | let parser = CP.noneOf("uncopyrightable", "wrong")
116 | Assert.assertSuccess(parser, "x", x)
117 | Assert.assertFailure(parser, "c", "wrong")
118 | Assert.assertFailure(parser, "g", "wrong")
119 | Assert.assertFailure(parser, "h", "wrong")
120 | Assert.assertFailure(parser, "i", "wrong")
121 | Assert.assertFailure(parser, "o", "wrong")
122 | Assert.assertFailure(parser, "p", "wrong")
123 | Assert.assertFailure(parser, "r", "wrong")
124 | Assert.assertFailure(parser, "t", "wrong")
125 | Assert.assertFailure(parser, "y", "wrong")
126 | }
127 |
128 | func testNoneOfEmpty() {
129 | let parser = CP.noneOf("")
130 | Assert.assertSuccess(parser, "a", a)
131 | Assert.assertSuccess(parser, "b", b)
132 | Assert.assertFailure(parser, "", "none of '' expected")
133 | }
134 |
135 | func testIs() {
136 | let parser = CP.of("a")
137 | Assert.assertSuccess(parser, "a", a)
138 | Assert.assertFailure(parser, "b", "'a' expected")
139 | Assert.assertFailure(parser, "", "'a' expected")
140 | }
141 |
142 | func testIsWithMessage() {
143 | let parser = CP.of("a", "wrong")
144 | Assert.assertSuccess(parser, "a", a)
145 | Assert.assertFailure(parser, "b", "wrong")
146 | Assert.assertFailure(parser, "", "wrong")
147 | }
148 |
149 | func testDigit() {
150 | let parser = CP.digit()
151 | Assert.assertSuccess(parser, "1", _1)
152 | Assert.assertSuccess(parser, "9", _9)
153 | Assert.assertFailure(parser, "a", "digit expected")
154 | Assert.assertFailure(parser, "", "digit expected")
155 | }
156 |
157 | func testDigitWithMessage() {
158 | let parser = CP.digit("wrong")
159 | Assert.assertSuccess(parser, "1", _1)
160 | Assert.assertSuccess(parser, "9", _9)
161 | Assert.assertFailure(parser, "a", "wrong")
162 | Assert.assertFailure(parser, "", "wrong")
163 | }
164 |
165 | func testLetter() {
166 | let parser = CP.letter()
167 | Assert.assertSuccess(parser, "a", a)
168 | Assert.assertSuccess(parser, "X", X)
169 | Assert.assertFailure(parser, "0", "letter expected")
170 | Assert.assertFailure(parser, "", "letter expected")
171 | }
172 |
173 | func testLetterWithMessage() {
174 | let parser = CP.letter("wrong")
175 | Assert.assertSuccess(parser, "a", a)
176 | Assert.assertSuccess(parser, "X", X)
177 | Assert.assertFailure(parser, "0", "wrong")
178 | Assert.assertFailure(parser, "", "wrong")
179 | }
180 |
181 | func testLowerCase() {
182 | let parser = CP.lowerCase()
183 | Assert.assertSuccess(parser, "a", a)
184 | Assert.assertFailure(parser, "A", "lowercase letter expected")
185 | Assert.assertFailure(parser, "0", "lowercase letter expected")
186 | Assert.assertFailure(parser, "", "lowercase letter expected")
187 | }
188 |
189 | func testLowerCaseWithMessage() {
190 | let parser = CP.lowerCase("wrong")
191 | Assert.assertSuccess(parser, "a", a)
192 | Assert.assertFailure(parser, "A", "wrong")
193 | Assert.assertFailure(parser, "0", "wrong")
194 | Assert.assertFailure(parser, "", "wrong")
195 | }
196 |
197 | func testPatternWithSingle() {
198 | let parser = CP.pattern("abc")
199 | Assert.assertSuccess(parser, "a", a)
200 | Assert.assertSuccess(parser, "b", b)
201 | Assert.assertSuccess(parser, "c", c)
202 | Assert.assertFailure(parser, "d", "[abc] expected")
203 | Assert.assertFailure(parser, "", "[abc] expected")
204 | }
205 |
206 | func testPatternWithMessage() {
207 | let parser = CP.pattern("abc", "wrong")
208 | Assert.assertSuccess(parser, "a", a)
209 | Assert.assertFailure(parser, "d", "wrong")
210 | Assert.assertFailure(parser, "", "wrong")
211 | }
212 |
213 | func testPatternWithRange() {
214 | let parser = CP.pattern("a-c")
215 | Assert.assertSuccess(parser, "a", a)
216 | Assert.assertSuccess(parser, "b", b)
217 | Assert.assertSuccess(parser, "c", c)
218 | Assert.assertFailure(parser, "d", "[a-c] expected")
219 | Assert.assertFailure(parser, "", "[a-c] expected")
220 | }
221 |
222 | func testPatternWithOverlappingRange() {
223 | let parser = CP.pattern("b-da-c")
224 | Assert.assertSuccess(parser, "a", a)
225 | Assert.assertSuccess(parser, "b", b)
226 | Assert.assertSuccess(parser, "c", c)
227 | Assert.assertSuccess(parser, "d", d)
228 | Assert.assertFailure(parser, "e", "[b-da-c] expected")
229 | Assert.assertFailure(parser, "", "[b-da-c] expected")
230 | }
231 |
232 | func testPatternWithAdjacentRange() {
233 | let parser = CP.pattern("c-ea-c")
234 | Assert.assertSuccess(parser, "a", a)
235 | Assert.assertSuccess(parser, "b", b)
236 | Assert.assertSuccess(parser, "c", c)
237 | Assert.assertSuccess(parser, "d", d)
238 | Assert.assertSuccess(parser, "e", e)
239 | Assert.assertFailure(parser, "f", "[c-ea-c] expected")
240 | Assert.assertFailure(parser, "", "[c-ea-c] expected")
241 | }
242 |
243 | func testPatternWithPrefixRange() {
244 | let parser = CP.pattern("a-ea-c")
245 | Assert.assertSuccess(parser, "a", a)
246 | Assert.assertSuccess(parser, "b", b)
247 | Assert.assertSuccess(parser, "c", c)
248 | Assert.assertSuccess(parser, "d", d)
249 | Assert.assertSuccess(parser, "e", e)
250 | Assert.assertFailure(parser, "f", "[a-ea-c] expected")
251 | Assert.assertFailure(parser, "", "[a-ea-c] expected")
252 | }
253 |
254 | func testPatternWithPostfixRange() {
255 | let parser = CP.pattern("a-ec-e")
256 | Assert.assertSuccess(parser, "a", a)
257 | Assert.assertSuccess(parser, "b", b)
258 | Assert.assertSuccess(parser, "c", c)
259 | Assert.assertSuccess(parser, "d", d)
260 | Assert.assertSuccess(parser, "e", e)
261 | Assert.assertFailure(parser, "f", "[a-ec-e] expected")
262 | Assert.assertFailure(parser, "", "[a-ec-e] expected")
263 | }
264 |
265 | func testPatternWithRepeatedRange() {
266 | let parser = CP.pattern("a-ea-e")
267 | Assert.assertSuccess(parser, "a", a)
268 | Assert.assertSuccess(parser, "b", b)
269 | Assert.assertSuccess(parser, "c", c)
270 | Assert.assertSuccess(parser, "d", d)
271 | Assert.assertSuccess(parser, "e", e)
272 | Assert.assertFailure(parser, "f", "[a-ea-e] expected")
273 | Assert.assertFailure(parser, "", "[a-ea-e] expected")
274 | }
275 |
276 | func testPatternWithComposed() {
277 | let parser = CP.pattern("ac-df-")
278 | Assert.assertSuccess(parser, "a", a)
279 | Assert.assertSuccess(parser, "c", c)
280 | Assert.assertSuccess(parser, "d", d)
281 | Assert.assertSuccess(parser, "f", f)
282 | Assert.assertSuccess(parser, "-", minus)
283 | Assert.assertFailure(parser, "b", "[ac-df-] expected")
284 | Assert.assertFailure(parser, "e", "[ac-df-] expected")
285 | Assert.assertFailure(parser, "g", "[ac-df-] expected")
286 | Assert.assertFailure(parser, "", "[ac-df-] expected")
287 | }
288 |
289 | func testPatternWithNegatedSingle() {
290 | let parser = CP.pattern("^a")
291 | Assert.assertSuccess(parser, "b", b)
292 | Assert.assertFailure(parser, "a", "[^a] expected")
293 | Assert.assertFailure(parser, "", "[^a] expected")
294 | }
295 |
296 | func testPatternWithNegatedRange() {
297 | let parser = CP.pattern("^a-c")
298 | Assert.assertSuccess(parser, "d", d)
299 | Assert.assertFailure(parser, "a", "[^a-c] expected")
300 | Assert.assertFailure(parser, "b", "[^a-c] expected")
301 | Assert.assertFailure(parser, "c", "[^a-c] expected")
302 | Assert.assertFailure(parser, "", "[^a-c] expected")
303 | }
304 |
305 | func testRange() {
306 | let parser = CP.range("e", "o")
307 | Assert.assertFailure(parser, "d", "e..o expected")
308 | Assert.assertSuccess(parser, "e", e)
309 | Assert.assertSuccess(parser, "i", i)
310 | Assert.assertSuccess(parser, "o", o)
311 | Assert.assertFailure(parser, "p", "e..o expected")
312 | Assert.assertFailure(parser, "", "e..o expected")
313 | }
314 |
315 | func testRangeWithMessage() {
316 | let parser = CP.range("e", "o", "wrong")
317 | Assert.assertFailure(parser, "d", "wrong")
318 | Assert.assertSuccess(parser, "e", e)
319 | Assert.assertSuccess(parser, "i", i)
320 | Assert.assertSuccess(parser, "o", o)
321 | Assert.assertFailure(parser, "p", "wrong")
322 | Assert.assertFailure(parser, "", "wrong")
323 | }
324 |
325 | func testUpperCase() {
326 | let parser = CP.upperCase()
327 | Assert.assertSuccess(parser, "X", X)
328 | Assert.assertFailure(parser, "x", "uppercase letter expected")
329 | Assert.assertFailure(parser, "0", "uppercase letter expected")
330 | Assert.assertFailure(parser, "", "uppercase letter expected")
331 | }
332 |
333 | func testUpperCaseWithMessage() {
334 | let parser = CP.upperCase("wrong")
335 | Assert.assertSuccess(parser, "X", X)
336 | Assert.assertFailure(parser, "x", "wrong")
337 | Assert.assertFailure(parser, "0", "wrong")
338 | Assert.assertFailure(parser, "", "wrong")
339 | }
340 |
341 | func testWhitespace() {
342 | let parser = CP.whitespace()
343 | Assert.assertSuccess(parser, " ", space)
344 | Assert.assertFailure(parser, "z", "whitespace expected")
345 | Assert.assertFailure(parser, "-", "whitespace expected")
346 | Assert.assertFailure(parser, "", "whitespace expected")
347 | }
348 |
349 | func testWhitespaceWithMessage() {
350 | let parser = CP.whitespace("wrong")
351 | Assert.assertSuccess(parser, " ", space)
352 | Assert.assertFailure(parser, "z", "wrong")
353 | Assert.assertFailure(parser, "-", "wrong")
354 | Assert.assertFailure(parser, "", "wrong")
355 | }
356 |
357 | func testWord() {
358 | let parser = CP.word()
359 | Assert.assertSuccess(parser, "a", a)
360 | Assert.assertSuccess(parser, "0", _0)
361 | Assert.assertFailure(parser, "-", "letter or digit expected")
362 | Assert.assertFailure(parser, "", "letter or digit expected")
363 | }
364 |
365 | func testWordWithMessage() {
366 | let parser = CP.word("wrong")
367 | Assert.assertSuccess(parser, "a", a)
368 | Assert.assertSuccess(parser, "0", _0)
369 | Assert.assertFailure(parser, "-", "wrong")
370 | Assert.assertFailure(parser, "", "wrong")
371 | }
372 | }
373 |
--------------------------------------------------------------------------------
/src/Tests/ParsersTest.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ParsersTest.swift
3 | // Tests
4 | //
5 | // Created by Philipp Arndt on 2019-12-20.
6 | // Copyright © 2019 petitparser. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import XCTest
11 | @testable import swift_petitparser
12 |
13 | // swiftlint:disable type_body_length nesting
14 | class ParsersTests: XCTestCase {
15 |
16 | func char(_ c: Character) -> Character {
17 | return c
18 | }
19 |
20 | func testAnd() {
21 | let parser = CP.of("a").and()
22 | Assert.assertSuccess(parser, "a", char("a"), 0)
23 | Assert.assertFailure(parser, "b", "'a' expected")
24 | Assert.assertFailure(parser, "")
25 | }
26 |
27 | func testChoice2() {
28 | let parser1 = CP.of("a").or(CP.of("b"))
29 | Assert.assertSuccess(parser1, "a", char("a"))
30 | Assert.assertSuccess(parser1, "b", char("b"))
31 | Assert.assertFailure(parser1, "c")
32 | Assert.assertFailure(parser1, "")
33 |
34 | let parser2 = CP.of("a") | CP.of("b")
35 | Assert.assertSuccess(parser2, "a", char("a"))
36 | Assert.assertSuccess(parser2, "b", char("b"))
37 | Assert.assertFailure(parser2, "c")
38 | Assert.assertFailure(parser2, "")
39 | }
40 |
41 | func testChoice3() {
42 | let parser1 = CP.of("a").or(CP.of("b")).or(CP.of("c"))
43 | Assert.assertSuccess(parser1, "a", char("a"))
44 | Assert.assertSuccess(parser1, "b", char("b"))
45 | Assert.assertSuccess(parser1, "c", char("c"))
46 | Assert.assertFailure(parser1, "d")
47 | Assert.assertFailure(parser1, "")
48 |
49 | let parser2 = CP.of("a") | CP.of("b") | CP.of("c")
50 | Assert.assertSuccess(parser2, "a", char("a"))
51 | Assert.assertSuccess(parser2, "b", char("b"))
52 | Assert.assertSuccess(parser2, "c", char("c"))
53 | Assert.assertFailure(parser2, "d")
54 | Assert.assertFailure(parser2, "")
55 | }
56 |
57 | func testEndOfInput() {
58 | let parser = CP.of("a").end()
59 | Assert.assertFailure(parser, "", "'a' expected")
60 | Assert.assertSuccess(parser, "a", char("a"))
61 | Assert.assertFailure(parser, "aa", 1, "end of input expected")
62 | }
63 |
64 | func testSettable() {
65 | let parser = CP.of("a").settable()
66 | Assert.assertSuccess(parser, "a", char("a"))
67 | Assert.assertFailure(parser, "b", 0, "'a' expected")
68 |
69 | parser.set(CP.of("b"))
70 | Assert.assertSuccess(parser, "b", char("b"))
71 | Assert.assertFailure(parser, "a", 0, "'b' expected")
72 | }
73 |
74 | func testFlatten1() {
75 | let parser = CP.digit().repeated(2, RepeatingParser.UNBOUNDED).flatten()
76 | Assert.assertFailure(parser, "", 0, "digit expected")
77 | Assert.assertFailure(parser, "a", 0, "digit expected")
78 | Assert.assertFailure(parser, "1", 1, "digit expected")
79 | Assert.assertFailure(parser, "1a", 1, "digit expected")
80 | Assert.assertSuccess(parser, "12", "12")
81 | Assert.assertSuccess(parser, "123", "123")
82 | Assert.assertSuccess(parser, "1234", "1234")
83 | }
84 |
85 | func testFlatten2() {
86 | let parser = CP.digit().repeated(2, RepeatingParser.UNBOUNDED)
87 | .flatten("gimme a number")
88 | Assert.assertFailure(parser, "", 0, "gimme a number")
89 | Assert.assertFailure(parser, "a", 0, "gimme a number")
90 | Assert.assertFailure(parser, "1", 0, "gimme a number")
91 | Assert.assertFailure(parser, "1a", 0, "gimme a number")
92 | Assert.assertSuccess(parser, "12", "12")
93 | Assert.assertSuccess(parser, "123", "123")
94 | Assert.assertSuccess(parser, "1234", "1234")
95 | }
96 |
97 | func testMap() {
98 | let parser = CP.digit()
99 | .map { (c: Character) -> Int in Int("\(c)") ?? -1 }
100 | Assert.assertSuccess(parser, "1", 1)
101 | Assert.assertSuccess(parser, "4", 4)
102 | Assert.assertSuccess(parser, "9", 9)
103 | Assert.assertFailure(parser, "")
104 | Assert.assertFailure(parser, "a")
105 | }
106 |
107 | func testPick() {
108 | let parser = CP.digit().seq(CP.letter()).pick(1)
109 | Assert.assertSuccess(parser, "1a", char("a"))
110 | Assert.assertSuccess(parser, "2b", char("b"))
111 | Assert.assertFailure(parser, "")
112 | Assert.assertFailure(parser, "1", 1, "letter expected")
113 | Assert.assertFailure(parser, "12", 1, "letter expected")
114 |
115 | let parser2 = (CP.digit() + CP.letter()).pick(1)
116 | Assert.assertSuccess(parser2, "1a", char("a"))
117 | Assert.assertSuccess(parser2, "2b", char("b"))
118 | Assert.assertFailure(parser2, "")
119 | Assert.assertFailure(parser2, "1", 1, "letter expected")
120 | Assert.assertFailure(parser2, "12", 1, "letter expected")
121 | }
122 |
123 | func testPickLast() {
124 | let parser = CP.digit().seq(CP.letter()).pick(-1)
125 | Assert.assertSuccess(parser, "1a", char("a"))
126 | Assert.assertSuccess(parser, "2b", char("b"))
127 | Assert.assertFailure(parser, "")
128 | Assert.assertFailure(parser, "1", 1, "letter expected")
129 | Assert.assertFailure(parser, "12", 1, "letter expected")
130 | }
131 |
132 | func testPermute() {
133 | let parser = CP.digit().seq(CP.letter()).permute(1, 0)
134 | Assert.assertSuccess(parser, "1a", [char("a"), char("1")])
135 | Assert.assertSuccess(parser, "2b", [char("b"), char("2")])
136 | Assert.assertFailure(parser, "")
137 | Assert.assertFailure(parser, "1", 1, "letter expected")
138 | Assert.assertFailure(parser, "12", 1, "letter expected")
139 | }
140 |
141 | func testPermuteLast() {
142 | let parser = CP.digit().seq(CP.letter()).permute(-1, 0)
143 | Assert.assertSuccess(parser, "1a", [char("a"), char("1")])
144 | Assert.assertSuccess(parser, "2b", [char("b"), char("2")])
145 | Assert.assertFailure(parser, "")
146 | Assert.assertFailure(parser, "1", 1, "letter expected")
147 | Assert.assertFailure(parser, "12", 1, "letter expected")
148 | }
149 |
150 | func testNeg1() {
151 | let parser = CP.digit().neg()
152 | Assert.assertFailure(parser, "1", 0)
153 | Assert.assertFailure(parser, "9", 0)
154 | Assert.assertSuccess(parser, "a", char("a"))
155 | Assert.assertSuccess(parser, " ", char(" "))
156 | Assert.assertFailure(parser, "", 0)
157 | }
158 |
159 | func testNeg2() {
160 | let parser = CP.digit().neg("no digit expected")
161 | Assert.assertFailure(parser, "1", 0, "no digit expected")
162 | Assert.assertFailure(parser, "9", 0, "no digit expected")
163 | Assert.assertSuccess(parser, "a", char("a"))
164 | Assert.assertSuccess(parser, " ", char(" "))
165 | Assert.assertFailure(parser, "", 0, "no digit expected")
166 | }
167 |
168 | func testNeg3() {
169 | let parser = SP.of("foo").neg("no foo expected")
170 | Assert.assertFailure(parser, "foo", 0, "no foo expected")
171 | Assert.assertFailure(parser, "foobar", 0, "no foo expected")
172 | Assert.assertSuccess(parser, "f", char("f"))
173 | Assert.assertSuccess(parser, " ", char(" "))
174 | }
175 |
176 | func testNot() {
177 | let parser = CP.of("a").not("not a expected")
178 | Assert.assertFailure(parser, "a", "not a expected")
179 | Assert.assertSuccess(parser, "b", Assert.NULL, 0)
180 | Assert.assertSuccess(parser, "", Assert.NULL)
181 | }
182 |
183 | func testNotAlternative() {
184 | let parser = !CP.of("a")
185 | Assert.assertFailure(parser, "a", "unexpected")
186 | Assert.assertSuccess(parser, "b", Assert.NULL, 0)
187 | Assert.assertSuccess(parser, "", Assert.NULL)
188 | }
189 |
190 | func testOptional() {
191 | let parser = CP.of("a").optional()
192 | Assert.assertSuccess(parser, "a", char("a"))
193 | Assert.assertSuccess(parser, "b", Assert.NULL, 0)
194 | Assert.assertSuccess(parser, "", Assert.NULL)
195 | }
196 |
197 | func testPlus() {
198 | let parser = CP.of("a").plus()
199 | Assert.assertFailure(parser, "", "'a' expected")
200 | Assert.assertSuccess(parser, "a", [char("a")])
201 | Assert.assertSuccess(parser, "aa", [char("a"), char("a")])
202 | Assert.assertSuccess(parser, "aaa", [char("a"), char("a"), char("a")])
203 | }
204 |
205 | func testPlusGreedy() {
206 | let parser = CP.word().plusGreedy(CharacterParser.digit())
207 | Assert.assertFailure(parser, "", 0, "letter or digit expected")
208 | Assert.assertFailure(parser, "a", 1, "digit expected")
209 | Assert.assertFailure(parser, "ab", 1, "digit expected")
210 | Assert.assertFailure(parser, "1", 1, "digit expected")
211 | Assert.assertSuccess(parser, "a1", [char("a")], 1)
212 | Assert.assertSuccess(parser, "ab1", [char("a"), char("b")], 2)
213 | Assert.assertSuccess(parser, "abc1", [char("a"), char("b"), char("c")], 3)
214 | Assert.assertSuccess(parser, "12", [char("1")], 1)
215 | Assert.assertSuccess(parser, "a12", [char("a"), char("1")], 2)
216 | Assert.assertSuccess(parser, "ab12", [char("a"), char("b"), char("1")], 3)
217 | Assert.assertSuccess(parser, "abc12", [char("a"), char("b"), char("c"), char("1")], 4)
218 | Assert.assertSuccess(parser, "123", [char("1"), char("2")], 2)
219 | Assert.assertSuccess(parser, "a123", [char("a"), char("1"), char("2")], 3)
220 | Assert.assertSuccess(parser, "ab123", [char("a"), char("b"), char("1"), char("2")], 4)
221 | Assert.assertSuccess(parser, "abc123", [char("a"), char("b"), char("c"), char("1"), char("2")], 5)
222 | }
223 |
224 | func testPlusLazy() {
225 | let parser = CP.word().plusLazy(CharacterParser.digit())
226 | Assert.assertFailure(parser, "")
227 | Assert.assertFailure(parser, "a", 1, "digit expected")
228 | Assert.assertFailure(parser, "ab", 2, "digit expected")
229 | Assert.assertFailure(parser, "1", 1, "digit expected")
230 | Assert.assertSuccess(parser, "a1", [char("a")], 1)
231 | Assert.assertSuccess(parser, "ab1", [char("a"), char("b")], 2)
232 | Assert.assertSuccess(parser, "abc1", [char("a"), char("b"), char("c")], 3)
233 | Assert.assertSuccess(parser, "12", [char("1")], 1)
234 | Assert.assertSuccess(parser, "a12", [char("a")], 1)
235 | Assert.assertSuccess(parser, "ab12", [char("a"), char("b")], 2)
236 | Assert.assertSuccess(parser, "abc12", [char("a"), char("b"), char("c")], 3)
237 | Assert.assertSuccess(parser, "123", [char("1")], 1)
238 | Assert.assertSuccess(parser, "a123", [char("a")], 1)
239 | Assert.assertSuccess(parser, "ab123", [char("a"), char("b")], 2)
240 | Assert.assertSuccess(parser, "abc123", [char("a"), char("b"), char("c")], 3)
241 | }
242 |
243 | func testTimes() {
244 | let parser = CP.of("a").times(2)
245 | Assert.assertFailure(parser, "", 0, "'a' expected")
246 | Assert.assertFailure(parser, "a", 1, "'a' expected")
247 | Assert.assertSuccess(parser, "aa", [char("a"), char("a")])
248 | Assert.assertSuccess(parser, "aaa", [char("a"), char("a")], 2)
249 | }
250 |
251 | func testRepeat() {
252 | let parser = CP.of("a").repeated(2, 3)
253 | Assert.assertFailure(parser, "", "'a' expected")
254 | Assert.assertFailure(parser, "a", 1, "'a' expected")
255 | Assert.assertSuccess(parser, "aa", [char("a"), char("a")])
256 | Assert.assertSuccess(parser, "aaa", [char("a"), char("a"), char("a")])
257 | Assert.assertSuccess(parser, "aaaa", [char("a"), char("a"), char("a")], 3)
258 | }
259 |
260 | // func testRepeatMinError1() {
261 | // let _ = CP.of("a").repeated(-2, 5)
262 | // }
263 | //
264 | // func testRepeatMinError2() {
265 | // let _ = CP.of("a").repeated(3, 5)
266 | // }
267 |
268 | func testRepeatUnbounded() {
269 | var list: [Character] = []
270 | var string = ""
271 | for _ in 0..<100000 {
272 | list.append("a")
273 | string += "a"
274 | }
275 |
276 | let parser = CP.of("a").repeated(2, RepeatingParser.UNBOUNDED)
277 | Assert.assertSuccess(parser, string, list)
278 | }
279 |
280 | func testRepeatGreedy() {
281 | let parser = CP.word().repeatGreedy(CP.digit(), 2, 4)
282 | Assert.assertFailure(parser, "", 0, "letter or digit expected")
283 | Assert.assertFailure(parser, "a", 1, "letter or digit expected")
284 | Assert.assertFailure(parser, "ab", 2, "digit expected")
285 | Assert.assertFailure(parser, "abc", 2, "digit expected")
286 | Assert.assertFailure(parser, "abcd", 2, "digit expected")
287 | Assert.assertFailure(parser, "abcde", 2, "digit expected")
288 | Assert.assertFailure(parser, "1", 1, "letter or digit expected")
289 | Assert.assertFailure(parser, "a1", 2, "digit expected")
290 | Assert.assertSuccess(parser, "ab1", [char("a"), char("b")], 2)
291 | Assert.assertSuccess(parser, "abc1", [char("a"), char("b"), char("c")], 3)
292 | Assert.assertSuccess(parser, "abcd1", [char("a"), char("b"), char("c"), char("d")], 4)
293 | Assert.assertFailure(parser, "abcde1", 2, "digit expected")
294 | Assert.assertFailure(parser, "12", 2, "digit expected")
295 | Assert.assertSuccess(parser, "a12", [char("a"), char("1")], 2)
296 | Assert.assertSuccess(parser, "ab12", [char("a"), char("b"), char("1")], 3)
297 | Assert.assertSuccess(parser, "abc12", [char("a"), char("b"), char("c"), char("1")], 4)
298 | Assert.assertSuccess(parser, "abcd12", [char("a"), char("b"), char("c"), char("d")], 4)
299 | Assert.assertFailure(parser, "abcde12", 2, "digit expected")
300 | Assert.assertSuccess(parser, "123", [char("1"), char("2")], 2)
301 | Assert.assertSuccess(parser, "a123", [char("a"), char("1"), char("2")], 3)
302 | Assert.assertSuccess(parser, "ab123", [char("a"), char("b"), char("1"), char("2")], 4)
303 | Assert.assertSuccess(parser, "abc123", [char("a"), char("b"), char("c"), char("1")], 4)
304 | Assert.assertSuccess(parser, "abcd123", [char("a"), char("b"), char("c"), char("d")], 4)
305 | Assert.assertFailure(parser, "abcde123", 2, "digit expected")
306 | }
307 |
308 | // func testRepeatGreedyUnbounded() {
309 | // var letter = ""
310 | // var listLetter: [Character] = []
311 | //
312 | // var digit = ""
313 | // var listDigit: [Character] = []
314 | //
315 | // for _ in 0..<100000 {
316 | // letter += "a"
317 | // listLetter += "a"
318 | //
319 | // digit += "1"
320 | // listDigit += "1"
321 | // }
322 | //
323 | // letter += "a"
324 | // digit += "1"
325 | //
326 | // let parser = CP.word()
327 | // .repeatGreedy(CP.digit(), 2, RepeatingParser.UNBOUNDED);
328 | // Assert.assertSuccess(parser, letter, listLetter, listLetter.count)
329 | // Assert.assertSuccess(parser, digit, listDigit, listDigit.count)
330 | // }
331 |
332 | func testRepeatLazy() {
333 | let parser = CP.word().repeatLazy(CP.digit(), 2, 4)
334 | Assert.assertFailure(parser, "", 0, "letter or digit expected")
335 | Assert.assertFailure(parser, "a", 1, "letter or digit expected")
336 | Assert.assertFailure(parser, "ab", 2, "digit expected")
337 | Assert.assertFailure(parser, "abc", 3, "digit expected")
338 | Assert.assertFailure(parser, "abcd", 4, "digit expected")
339 | Assert.assertFailure(parser, "abcde", 4, "digit expected")
340 | Assert.assertFailure(parser, "1", 1, "letter or digit expected")
341 | Assert.assertFailure(parser, "a1", 2, "digit expected")
342 | Assert.assertSuccess(parser, "ab1", [char("a"), char("b")], 2)
343 | Assert.assertSuccess(parser, "abc1", [char("a"), char("b"), char("c")], 3)
344 | Assert.assertSuccess(parser, "abcd1", [char("a"), char("b"), char("c"), char("d")], 4)
345 | Assert.assertFailure(parser, "abcde1", 4, "digit expected")
346 | Assert.assertFailure(parser, "12", 2, "digit expected")
347 | Assert.assertSuccess(parser, "a12", [char("a"), char("1")], 2)
348 | Assert.assertSuccess(parser, "ab12", [char("a"), char("b")], 2)
349 | Assert.assertSuccess(parser, "abc12", [char("a"), char("b"), char("c")], 3)
350 | Assert.assertSuccess(parser, "abcd12", [char("a"), char("b"), char("c"), char("d")], 4)
351 | Assert.assertFailure(parser, "abcde12", 4, "digit expected")
352 | Assert.assertSuccess(parser, "123", [char("1"), char("2")], 2)
353 | Assert.assertSuccess(parser, "a123", [char("a"), char("1")], 2)
354 | Assert.assertSuccess(parser, "ab123", [char("a"), char("b")], 2)
355 | Assert.assertSuccess(parser, "abc123", [char("a"), char("b"), char("c")], 3)
356 | Assert.assertSuccess(parser, "abcd123", [char("a"), char("b"), char("c"), char("d")], 4)
357 | Assert.assertFailure(parser, "abcde123", 4, "digit expected")
358 | }
359 |
360 | // @Test
361 | // public void testRepeatLazyUnbounded() {
362 | // StringBuilder builder = new StringBuilder();
363 | // List list = new ArrayList<>();
364 | // for (int i = 0; i < 100000; i++) {
365 | // builder.append('a');
366 | // list.add('a');
367 | // }
368 | // builder.append("1111");
369 | // Parser parser = CharacterParser.word()
370 | // .repeatLazy(CharacterParser.digit(), 2, RepeatingParser.UNBOUNDED);
371 | // assertSuccess(parser, builder.toString(), list, list.size());
372 | // }
373 | //
374 | // @Test
375 | // public void testSequence2() {
376 | // Parser parser = of('a').seq(of('b'));
377 | // assertSuccess(parser, "ab", Arrays.asList('a', 'b'));
378 | // assertFailure(parser, "");
379 | // assertFailure(parser, "x");
380 | // assertFailure(parser, "a", 1);
381 | // assertFailure(parser, "ax", 1);
382 | // }
383 | //
384 | // @Test
385 | // public void testSequence3() {
386 | // Parser parser = of('a').seq(of('b')).seq(of('c'));
387 | // assertSuccess(parser, "abc", Arrays.asList('a', 'b', 'c'));
388 | // assertFailure(parser, "");
389 | // assertFailure(parser, "x");
390 | // assertFailure(parser, "a", 1);
391 | // assertFailure(parser, "ax", 1);
392 | // assertFailure(parser, "ab", 2);
393 | // assertFailure(parser, "abx", 2);
394 | // }
395 | //
396 |
397 | func testStar() {
398 | let parser = CP.of("a").star()
399 | Assert.assertSuccess(parser, "", [])
400 | Assert.assertSuccess(parser, "a", [char("a")])
401 | Assert.assertSuccess(parser, "aa", [char("a"), char("a")])
402 | Assert.assertSuccess(parser, "aaa", [char("a"), char("a"), char("a")])
403 | }
404 |
405 | func testStarGreedy() {
406 | let parser = CP.word().starGreedy(CP.digit())
407 | Assert.assertFailure(parser, "", 0, "digit expected")
408 | Assert.assertFailure(parser, "a", 0, "digit expected")
409 | Assert.assertFailure(parser, "ab", 0, "digit expected")
410 | Assert.assertSuccess(parser, "1", Assert.EMPTY, 0)
411 | Assert.assertSuccess(parser, "a1", [char("a")], 1)
412 | Assert.assertSuccess(parser, "ab1", [char("a"), char("b")], 2)
413 | Assert.assertSuccess(parser, "abc1", [char("a"), char("b"), char("c")], 3)
414 | Assert.assertSuccess(parser, "12", [char("1")], 1)
415 | Assert.assertSuccess(parser, "a12", [char("a"), char("1")], 2)
416 | Assert.assertSuccess(parser, "ab12", [char("a"), char("b"), char("1")], 3)
417 | Assert.assertSuccess(parser, "abc12", [char("a"), char("b"), char("c"), char("1")], 4)
418 | Assert.assertSuccess(parser, "123", [char("1"), char("2")], 2)
419 | Assert.assertSuccess(parser, "a123", [char("a"), char("1"), char("2")], 3)
420 | Assert.assertSuccess(parser, "ab123", [char("a"), char("b"), char("1"), char("2")], 4)
421 | Assert.assertSuccess(parser, "abc123", [char("a"), char("b"), char("c"), char("1"), char("2")], 5)
422 | }
423 |
424 | func testStarLazy() {
425 | let parser = CP.word().starLazy(CP.digit())
426 | Assert.assertFailure(parser, "")
427 | Assert.assertFailure(parser, "a", 1, "digit expected")
428 | Assert.assertFailure(parser, "ab", 2, "digit expected")
429 | Assert.assertSuccess(parser, "1", Assert.EMPTY, 0)
430 | Assert.assertSuccess(parser, "a1", [char("a")], 1)
431 | Assert.assertSuccess(parser, "ab1", [char("a"), char("b")], 2)
432 | Assert.assertSuccess(parser, "abc1", [char("a"), char("b"), char("c")], 3)
433 | Assert.assertSuccess(parser, "12", Assert.EMPTY, 0)
434 | Assert.assertSuccess(parser, "a12", [char("a")], 1)
435 | Assert.assertSuccess(parser, "ab12", [char("a"), char("b")], 2)
436 | Assert.assertSuccess(parser, "abc12", [char("a"), char("b"), char("c")], 3)
437 | Assert.assertSuccess(parser, "123", Assert.EMPTY, 0)
438 | Assert.assertSuccess(parser, "a123", [char("a")], 1)
439 | Assert.assertSuccess(parser, "ab123", [char("a"), char("b")], 2)
440 | Assert.assertSuccess(parser, "abc123", [char("a"), char("b"), char("c")], 3)
441 | }
442 |
443 | func testToken() {
444 | let parser = CP.of("a").star().token().trim()
445 | let token: Token = parser.parse(" aa ").get()!
446 | XCTAssertEqual(1, token.start.utf16Offset(in: " aa "))
447 | XCTAssertEqual(3, token.stop.utf16Offset(in: " aa "))
448 | // Assert.assertEquals([char("a"), char("a")], token.>getValue()]
449 | }
450 |
451 | func testTrim() {
452 | let parser = CP.of("a").trim()
453 | Assert.assertSuccess(parser, "a", char("a"))
454 | Assert.assertSuccess(parser, " a", char("a"))
455 | Assert.assertSuccess(parser, "a ", char("a"))
456 | Assert.assertSuccess(parser, " a ", char("a"))
457 | Assert.assertSuccess(parser, " a", char("a"))
458 | Assert.assertSuccess(parser, "a ", char("a"))
459 | Assert.assertSuccess(parser, " a ", char("a"))
460 | Assert.assertFailure(parser, "", "'a' expected")
461 | Assert.assertFailure(parser, "b", "'a' expected")
462 | Assert.assertFailure(parser, " b", 1, "'a' expected")
463 | Assert.assertFailure(parser, " b", 2, "'a' expected")
464 | }
465 |
466 | func testTrimCustom() {
467 | let parser = CP.of("a").trim(CP.of("*"))
468 | Assert.assertSuccess(parser, "a", char("a"))
469 | Assert.assertSuccess(parser, "*a", char("a"))
470 | Assert.assertSuccess(parser, "a*", char("a"))
471 | Assert.assertSuccess(parser, "*a*", char("a"))
472 | Assert.assertSuccess(parser, "**a", char("a"))
473 | Assert.assertSuccess(parser, "a**", char("a"))
474 | Assert.assertSuccess(parser, "**a**", char("a"))
475 | Assert.assertFailure(parser, "", "'a' expected")
476 | Assert.assertFailure(parser, "b", "'a' expected")
477 | Assert.assertFailure(parser, "*b", 1, "'a' expected")
478 | Assert.assertFailure(parser, "**b", 2, "'a' expected")
479 | }
480 |
481 | func testSeparatedBy() {
482 | let parser = CP.of("a").separatedBy(CP.of("b"))
483 | Assert.assertFailure(parser, "", "'a' expected")
484 | Assert.assertSuccess(parser, "a", [char("a")])
485 | Assert.assertSuccess(parser, "ab", [char("a")], 1)
486 | Assert.assertSuccess(parser, "aba", [char("a"), char("b"), char("a")])
487 | Assert.assertSuccess(parser, "abab", [char("a"), char("b"), char("a")], 3)
488 | Assert.assertSuccess(parser, "ababa", [char("a"), char("b"), char("a"), char("b"), char("a")])
489 | Assert.assertSuccess(parser, "ababab", [char("a"), char("b"), char("a"), char("b"), char("a")], 5)
490 | }
491 |
492 | func testDelimitedBy() {
493 | let parser = CP.of("a").delimitedBy(CP.of("b"))
494 | Assert.assertFailure(parser, "", "'a' expected")
495 | Assert.assertSuccess(parser, "a", [char("a")])
496 | Assert.assertSuccess(parser, "ab", [char("a"), char("b")])
497 | Assert.assertSuccess(parser, "aba", [char("a"), char("b"), char("a")])
498 | Assert.assertSuccess(parser, "abab", [char("a"), char("b"), char("a"), char("b")])
499 | Assert.assertSuccess(parser, "ababa", [char("a"), char("b"), char("a"), char("b"), char("a")])
500 | Assert.assertSuccess(parser, "ababab",
501 | [char("a"), char("b"), char("a"), char("b"), char("a"), char("b")])
502 | }
503 |
504 | func testContinuationDelegating() {
505 | struct Continuation: ContinuationHandler {
506 | func apply(_ continuation: (Context) -> Result, _ context: Context) -> Result {
507 | return continuation(context)
508 | }
509 | }
510 |
511 | let parser = CharacterParser.digit().callCC(Continuation())
512 | XCTAssertTrue(parser.parse("1").isSuccess())
513 | XCTAssertFalse(parser.parse("a").isSuccess())
514 | }
515 |
516 | func testContinuationRedirecting() {
517 | struct Continuation: ContinuationHandler {
518 | func apply(_ continuation: (Context) -> Result, _ context: Context) -> Result {
519 | return CP.letter().parseOn(context)
520 | }
521 | }
522 |
523 | let parser = CharacterParser.digit().callCC(Continuation())
524 | XCTAssertFalse(parser.parse("1").isSuccess())
525 | XCTAssertTrue(parser.parse("a").isSuccess())
526 | }
527 |
528 | func testContinuationResuming() {
529 | class Continuation: ContinuationHandler {
530 | var continuations: [(Context) -> Result] = []
531 | var contexts: [Context] = []
532 |
533 | func apply(_ continuation: @escaping (Context) -> Result, _ context: Context) -> Result {
534 | self.continuations.append(continuation)
535 | self.contexts.append(context)
536 |
537 | // we have to return something for now
538 | return context.failure("Abort")
539 | }
540 | }
541 | let continuation = Continuation()
542 | let parser = CP.digit().callCC(continuation)
543 |
544 | // execute the parser twice to collect the continuations
545 | XCTAssertFalse(parser.parse("1").isSuccess())
546 | XCTAssertFalse(parser.parse("a").isSuccess())
547 |
548 | // later we can execute the captured continuations
549 | XCTAssertTrue(continuation.continuations[0](continuation.contexts[0]).isSuccess())
550 | XCTAssertFalse(continuation.continuations[1](continuation.contexts[1]).isSuccess())
551 |
552 | // of course the continuations can be resumed multiple times
553 | XCTAssertTrue(continuation.continuations[0](continuation.contexts[0]).isSuccess())
554 | XCTAssertFalse(continuation.continuations[1](continuation.contexts[1]).isSuccess())
555 | }
556 |
557 | func testContinuationSuccessful() {
558 | struct Continuation: ContinuationHandler {
559 | func apply(_ continuation: (Context) -> Result, _ context: Context) -> Result {
560 | return context.success("Always succeed")
561 | }
562 | }
563 |
564 | let parser = CharacterParser.digit()
565 | .callCC(Continuation())
566 |
567 | XCTAssertTrue(parser.parse("1").isSuccess())
568 | XCTAssertTrue(parser.parse("a").isSuccess())
569 | }
570 |
571 | func testContinuationFailing() {
572 | struct Continuation: ContinuationHandler {
573 | func apply(_ continuation: (Context) -> Result, _ context: Context) -> Result {
574 | return context.failure("Always fail")
575 | }
576 | }
577 |
578 | let parser = CharacterParser.digit()
579 | .callCC(Continuation())
580 |
581 | XCTAssertFalse(parser.parse("1").isSuccess())
582 | XCTAssertFalse(parser.parse("a").isSuccess())
583 | }
584 |
585 | func testSequence() {
586 | let parser = SequenceParser(CP.digit(), CP.word())
587 | XCTAssertTrue(parser.parse("1a").isSuccess())
588 | XCTAssertFalse(parser.parse("a1").isSuccess())
589 | }
590 | }
591 |
--------------------------------------------------------------------------------