-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
164 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
# SParser | ||
SParser creates easy to use file or string parsers in swift code using a file format that is based on [Backus–Naur form](https://en.wikipedia.org/wiki/Backus–Naur_form) | ||
|
||
To user Sparser: | ||
1. Create a .sparser file that describes the file format. | ||
2. Run SParser <filename> to compile the .sparser file to .sparser.swift. | ||
3. compile .sparser.swift with the code that uses the .sparser along with SParserLibs. | ||
|
||
## .sparser file format | ||
|
||
The SParser parser is written in SParser, which provides an exsample of how to use it. Quotes are from this file. See [SParser.sparser](https://github.com/amnykon/SParser/blob/master/Sources/SParserPrivate/SParser.sparser). | ||
|
||
When compiled, Sparser.sparser generates [SParser.sparser.swift](https://github.com/amnykon/SParser/blob/master/Sources/SParserPrivate/SParser.sparser) | ||
|
||
The file contains an optinal imports rule and one or more rules. | ||
|
||
### Import rule | ||
|
||
The import rule converts each import in to a swift import statement, allowing the generated swift code to use other packages. | ||
|
||
SParserLibs provides the Parser, Streams objects required to parse the file. This should always be used. | ||
|
||
``` SParser | ||
imports | ||
Foundation | ||
SParserLibs | ||
``` | ||
|
||
``` swift | ||
import Foundation | ||
import SParserLibs | ||
``` | ||
|
||
### Rules | ||
A SParser file contains rules that describe the file format. | ||
|
||
Each rule has the following: | ||
1. Name | ||
2. Type | ||
3. One or more patterns. | ||
|
||
``` SParser | ||
rules (1) | ||
type (2) | ||
[Rule] (2) | ||
::= rules rule (3) | ||
return rules + [rule] (3) | ||
::= (3) | ||
return [] (3) | ||
rule (1) | ||
type (2) | ||
Rule (2) | ||
::= name "\n" indent type patterns dedent (3) | ||
return Rule(name: name, type: type, patterns: patterns) (3) | ||
``` | ||
|
||
Each rule is compiled to a to a Parser typealias of the type, and Parser read function. | ||
|
||
``` swift | ||
extension Parser { | ||
public typealias RuleType = [String] | ||
public func readRule() throws -> RuleType? { | ||
``` | ||
### Patterns | ||
Patterns describe a expected sequence for the rule and an evaluator to run when the pattern matches what is read from the file. | ||
|
||
A pattern consists of: | ||
1. "::=" | ||
2. Zero or more terms. | ||
3. A evaluator that is ran when the pattern matches. | ||
|
||
``` SParser | ||
patterns | ||
type | ||
[Pattern] | ||
::=(1) pattern(2) patterns(2) | ||
return [pattern] + patterns (3) | ||
::=(1) (2) | ||
return [] (3) | ||
pattern | ||
type | ||
Pattern | ||
::=(1) "::="(2) cws(2) terms(2) "\n"(2) multiLineString(2) | ||
return Pattern(terms: terms, evaluator: multiLineString) (3) | ||
``` | ||
|
||
Each evaluator is compiled to an eval swift function. | ||
|
||
``` swift | ||
fileprivate func evalPatterns(pattern: Parser.PatternType, patterns: Parser.PatternsType) -> Parser.PatternsType { | ||
return [pattern] + patterns | ||
} | ||
|
||
fileprivate func evalPatterns() -> Parser.PatternsType { | ||
return [] | ||
} | ||
``` | ||
|
||
### Terms | ||
A Term describes a componet in a pattern. There are two types. | ||
1. String matching terms | ||
|
||
string matching terms are strings surounded by ", such as the "::=" term in the pattern rule. | ||
2. Rule matching terms. | ||
Rule matching terms use the name of a rule. This | ||
``` SParser | ||
::= name(1) "\n"(2) indent(2) type(2) patterns(2) dedent(2) | ||
``` | ||
Each term calls it's read function calls: | ||
1. Matches(string) for quoted terms | ||
2. The generated by the .cparser file | ||
3. Parser function named read<Type>(). | ||
``` | ||
public func readType() throws -> TypeType? { | ||
if matches(string: "type\n") { | ||
if let indent = try readIndent() { | ||
if let line = try readLine() { | ||
if let dedent = try readDedent() { | ||
return try recursivelyRead(type: evalType(indent: indent, line: line, dedent: dedent)) | ||
} | ||
try throwError(message:"error parsing type. expect dedent") | ||
} | ||
try throwError(message:"error parsing type. expect line") | ||
} | ||
try throwError(message:"error parsing type. expect indent") | ||
} | ||
return nil | ||
} | ||
``` | ||
## Include SParser in a package file | ||
set as a dependency in the Package.swift file: | ||
``` swift | ||
let package = Package( | ||
name: "MyProject", | ||
dependencies: [ | ||
.Package(url: "https://github.com/amnykon/SParser.git", majorVersion: 0), | ||
... | ||
] | ||
... | ||
) | ||
``` | ||
|
||
## Compile a .sparser file | ||
Build Sparser | ||
|
||
``` bash | ||
swift build | ||
``` | ||
|
||
Run SParser <filename> | ||
|
||
This will create a .swift file in the same location. | ||
``` bash | ||
SParser Source/SParserPrivate/SParser.sparser | ||
``` | ||
|