Commit 98a9625c authored by Drew's avatar Drew

Merge branch 'master' of code.sealedabstract.com:drewcrawford/StandBack

parents 9f1d3580 3fea1395
Pipeline #2490 failed with stage
in 1 minute and 12 seconds
......@@ -19,4 +19,20 @@ linux:
tags:
- autoscale-linux
image: drewcrawford/buildbase:latest
\ No newline at end of file
image: drewcrawford/buildbase:latest
docs:
stage: test
script:
- cd docs
- make html
- make dashdoc
artifacts:
paths:
- docs/_build/html
- docs/_build/StandBack.docset
tags:
- autoscale-linux
image: drewcrawford/anarchy-sphinx:latest
\ No newline at end of file
[![Anarchy Tools compatible](https://img.shields.io/badge/Anarchy%20Tools-compatible-4BC51D.svg?style=flat)](http://anarchytools.org)
![License:apache](https://img.shields.io/hexpm/l/plug.svg)
![Swift:3](https://img.shields.io/badge/Swift-3-blue.svg)
![Platform:macOS](https://img.shields.io/badge/Platform-macOS-red.svg)
![Platform:Linux](https://img.shields.io/badge/Platform-Linux-red.svg)
# StandBack
![StandBack](art/standback-small.png)
StandBack is a regular expression engine implementing the `egrep` (POSIX-extended) language. It is cross-platform and has no dependencies.
While `egrep` is a less popular language than PCRE, it is fully capable for basic programming tasks, and our API is *much* easier to use than Foundation's.
Here's a sample to get started. For more information, see our [documentation](http://standback-docs.sealedabstract.com).
```swift
let r = try! Regex(pattern: "class[[:space:]]+([[:alnum:]]+)[[:space:]]*:CarolineTest[[:space:]]*\\{")
print(try! r.match("prefix stuff class Foo:CarolineTest {"))
```
\ No newline at end of file
Installation
=============
To use StandBack, use `Anarchy Tools <http://anarchytools.org>`_.
Add this to your build.atpkg:
.. code-block:: clojure
:external-packages [
{
version [">=0.1"]
:url "https://code.sealedabstract.com/drewcrawford/StandBack.git"
}
]
Make sure to link with :code:`StandBack.a`.
\ No newline at end of file
......@@ -6,7 +6,7 @@ SPHINXOPTS =
SPHINXBUILD = sphinx-build
PAPER =
BUILDDIR = _build
PROJECT_NAME=Caroline
PROJECT_NAME=StandBack
export LC_ALL=C.UTF-8
export LANG=C.UTF-8
......
......@@ -50,7 +50,7 @@ import sphinx_bootstrap_theme
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
#html_logo = "caroline.png"
html_logo = "standback-24.png"
# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
......
......@@ -23,7 +23,8 @@ Contents:
.. toctree::
:maxdepth: 2
Installation
APIReference
......
......@@ -168,10 +168,15 @@ private final class RegexImp {
///A match object.
public struct Match : CustomStringConvertible, CustomDebugStringConvertible {
/// The start of the match
/// The start of the match (utf8 encoding)
public let start : Int
/// The end of the match
/// The end of the match (utf8 encoding)
public let end : Int
/// The length in UTF8 bytes of the match
public var length: Int {
return end - start
}
///The string we searched to generate this match
public let underlyingString : String
......@@ -219,7 +224,9 @@ public class FindResultGenerator: IteratorProtocol {
public func next() -> FindResultGenerator.Element? {
let startPosition = lastStart + (lastMatch?.end ?? 0) + 1
lastStart = startPosition
let proposedStartIndex = string.utf8.index(string.utf8.startIndex, offsetBy: startPosition, limitedBy: string.utf8.endIndex)!
guard let proposedStartIndex = string.utf8.index(string.utf8.startIndex, offsetBy: startPosition, limitedBy: string.utf8.endIndex) else {
return nil //index beyond range
}
let abbreviatedString = String(string.utf8[proposedStartIndex..<string.utf8.endIndex])!
let result = try! regex.findFirst(inString: abbreviatedString)
lastMatch = result?.entireMatch
......@@ -240,6 +247,13 @@ public class FindResultSequence: Sequence {
///A regular expression
public struct Regex {
private let regexImp : RegexImp
///Create a regex following the given pattern.
///For regex syntax, consult the extended regular expression specification at http://pubs.opengroup.org/onlinepubs/7908799/xbd/re.html
///For casual use, Boost has a particularly good guide: http://www.boost.org/doc/libs/1_54_0/libs/regex/doc/html/boost_regex/syntax/basic_extended.html
///- note: There are two levels of indirection here. For Swift literals, Swift-level escaping is applied first (e.g. `\\` -> `\`).
/// Therefore to escape through both systems, you may need `\\\\`.
/// See Swift's documentation on this here https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/StringsAndCharacters.html
public init(pattern: String) throws {
regexImp = try RegexImp(pattern: pattern)
}
......@@ -275,4 +289,56 @@ public struct Regex {
public func findAll(inString string: String) -> FindResultSequence {
return FindResultSequence(regex: self, string: string)
}
/// Replace all matches of a string by using a closure.
/// This allows powerful searching without the need to do advanced string edit logic by hand.
/// - parameter string: The string in which to search.
/// - parameter closure: A closure that will be passed `FindResult`s. Return the desired replacement value for each FindResult.
public func replaceAll(inString string: String, usingClosure closure: (FindResult) throws -> String) rethrows -> String {
var replaced = string
//For substitiutions of a different length than the source string, the string will change size as we read it
var offset = 0
for match in self.findAll(inString: string) {
//find existing length
let length = match.entireMatch.end - match.entireMatch.start
//take the part up the current match
var new_newString = String(replaced.utf8[replaced.utf8.startIndex ..< replaced.utf8.index(replaced.utf8.startIndex, offsetBy: match.entireMatch.start + offset)])!
//take the new part
let newString = try closure(match)
new_newString += newString
//take the part after the current match
new_newString += String(replaced.utf8[replaced.utf8.index(replaced.utf8.startIndex, offsetBy: match.entireMatch.end + offset) ..< replaced.utf8.endIndex])!
//calculate the new offset
offset += (length - newString.utf8.count)
replaced = new_newString
}
return replaced
}
/// Replace all matches of a regex with another string
/// - parameter string: We use the `entireMatch` of this value
/// - parameter newString: We replace the `entireMatch` with this new value
public func replaceAll(inString string: String, withNewString newString: String) -> String {
return self.replaceAll(inString: string, usingClosure: {j in return newString})
}
/// Replace the first match with another string
/// - parameter string: We use the `entireMatch` of this value
/// - parameter newString: We replace the `entireMatch` with thew new value
public func replaceFirst(inString string: String, withNewString newString: String) -> String {
var replaced = string
if let match = try! self.findFirst(inString: string) {
//take the part up the current match
var new_newString = String(replaced.utf8[replaced.utf8.startIndex ..< replaced.utf8.index(replaced.utf8.startIndex, offsetBy: match.entireMatch.start)])!
//take the new part
let newString = newString
new_newString += newString
//take the part after the current match
new_newString += String(replaced.utf8[replaced.utf8.index(replaced.utf8.startIndex, offsetBy: match.entireMatch.end) ..< replaced.utf8.endIndex])!
return new_newString
}
return replaced
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment