Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .swift-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
swift-4.2-DEVELOPMENT-SNAPSHOT-2018-08-15-a
swift-4.2
12 changes: 6 additions & 6 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ let package = Package(
targets: ["STLR"]
),
.library(
name: "ExampleLanguages",
name: "TestingSupport",
type: .static,
targets: ["ExampleLanguages"]
targets: ["TestingSupport"]
),
.executable(
//Swift Tool for Language Recognition Command
Expand All @@ -45,16 +45,16 @@ let package = Package(
name: "stlrc",
dependencies: ["OysterKit","STLR"]),
.target(
name: "ExampleLanguages",
name: "TestingSupport",
dependencies: ["OysterKit","STLR"]),
.testTarget(
name: "OysterKitTests",
dependencies: ["OysterKit","ExampleLanguages","STLR"]),
dependencies: ["OysterKit","TestingSupport","STLR"]),
.testTarget(
name: "STLRTests",
dependencies: ["OysterKit","ExampleLanguages","STLR"]),
dependencies: ["OysterKit","TestingSupport","STLR"]),
.testTarget(
name: "PerformanceTests",
dependencies: ["OysterKit","ExampleLanguages","STLR"]),
dependencies: ["OysterKit","TestingSupport","STLR"]),
]
)
20 changes: 20 additions & 0 deletions Resources/AnnotationTest.stlr
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/************************************************************

Grammar for testing annotations are correctly propagated

************************************************************/


grammar AnnotationTest

@terminal(true) terminal = "term " // Terminal token should have @terminal(true) annotation
@group(true) group = (terminal) // Group token should have @group(true) annotation, nested Terminal token should have @terminal(true) annotation
@identifier(true) identifier = terminal // Identifer token should have @identifier(true) annotation, nested terminal token should have @terminal(true) annotation
@recursive(true) recursive = "recurse " recursiveItem // Each recursive instance should have @recursive(true) annotation
~recursiveItem = recursive
// Each token should be as defined
normal = terminal group identifier recursive

// All tokens generated should have false override their default true except recursive where only the first should be false
overrides = @terminal(false) terminal @group(false) group @identifier(false) identifier @recursive(false) recursive

2 changes: 1 addition & 1 deletion Sources/OysterKit/Rules/Behaviour.swift
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ public struct Behaviour {
}

/**
Takes a description of a match and wraps it in the behavioural annotations of the rule
Takes a description of a match and wraps it in the annotations of the rule

- Parameter match: A description of the match
- Returns: A wrapped description of the match which includes the behaviour
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,60 @@

import Foundation

extension RuleAnnotation : Comparable {
/// I decide what the order of things are. And this is what I have decided.
private var sortValue : Int {
switch self {
case .token:
return 2
case .error:
return 5
case .void:
return 0
case .transient:
return 1
case .pinned:
return 3
case .type:
return 4
case .custom(_):
return 10
}
}

private var customLabelValue : String? {
if case let RuleAnnotation.custom(label) = self {
return label
}
return nil
}

/**
Compares two rule annotations. Standard annotations go first then custom ones sorted
by name.

- Parameter lhs: Left hand side of the <
- Parameter rhs: Right hand side of the <
- Returns: `true` if lhs < rhs
**/
public static func < (lhs : RuleAnnotation, rhs:RuleAnnotation) -> Bool {
if let lhs = lhs.customLabelValue, let rhs = rhs.customLabelValue {
return lhs < rhs
}

return lhs.sortValue < rhs.sortValue
}
}

extension Dictionary where Key == RuleAnnotation, Value == RuleAnnotationValue {
/// Generates a STLR like description of the annotations
var description : String {
return self.map({ (key,value) -> String in
return self.sorted(by: { (lhs, rhs) -> Bool in
return lhs.key < rhs.key
}).map({ (entry) -> String in
var result = ""

switch key {
switch entry.key {
case .token:
result = "@token"
case .error:
Expand All @@ -47,7 +94,7 @@ extension Dictionary where Key == RuleAnnotation, Value == RuleAnnotationValue {
result = "@\(label)"
}

switch value {
switch entry.value {

case .string(let string):
result += "(\"\(string)\")"
Expand Down
2 changes: 1 addition & 1 deletion Sources/STLR/Generators/Swift/SwiftStructure.swift
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ public class SwiftStructure : Generator{
"return try ParsingDecoder().decode(\(name).self, using: root)").outdent().print(
"}",
"",
"\(accessLevel) static var generatedLanguage : Grammar {return Parser(grammar:\(name)Tokens.generatedRules)}"
"\(accessLevel) static var generatedLanguage : Grammar {return \(name)Tokens.generatedRules}"
)

output.outdent().print("}")
Expand Down
139 changes: 139 additions & 0 deletions Sources/TestingSupport/AnnotationTest.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import Foundation
import OysterKit

/// Intermediate Representation of the grammar
internal enum AnnotationTestTokens : Int, TokenType, CaseIterable, Equatable {
typealias T = AnnotationTestTokens
// Cache for compiled regular expressions
private static var regularExpressionCache = [String : NSRegularExpression]()

/// Returns a pre-compiled pattern from the cache, or if not in the cache builds
/// the pattern, caches and returns the regular expression
///
/// - Parameter pattern: The pattern the should be built
/// - Returns: A compiled version of the pattern
///
private static func regularExpression(_ pattern:String)->NSRegularExpression{
if let cached = regularExpressionCache[pattern] {
return cached
}
do {
let new = try NSRegularExpression(pattern: pattern, options: [])
regularExpressionCache[pattern] = new
return new
} catch {
fatalError("Failed to compile pattern /\(pattern)/\n\(error)")
}
}
/// The tokens defined by the grammar
case `terminal`, `group`, `identifier`, `recursive`, `recursiveItem`, `normal`, `overrides`

/// The rule for the token
var rule : Rule {
switch self {
/// terminal
case .terminal:
return "term ".reference(.structural(token: self), annotations: [RuleAnnotation.custom(label:"terminal"):RuleAnnotationValue.bool(true)])

/// group
case .group:
return T.terminal.rule.reference(.structural(token: self), annotations: [RuleAnnotation.custom(label:"group"):RuleAnnotationValue.bool(true)])

/// identifier
case .identifier:
return T.terminal.rule.reference(.structural(token: self), annotations: [RuleAnnotation.custom(label:"identifier"):RuleAnnotationValue.bool(true)])

/// recursive
case .recursive:
guard let cachedRule = T.leftHandRecursiveRules[self.rawValue] else {
// Create recursive shell
let recursiveRule = RecursiveRule(stubFor: Behaviour(.structural(token: self), cardinality: Cardinality.one), with: [RuleAnnotation.custom(label:"recursive"):RuleAnnotationValue.bool(true)])
T.leftHandRecursiveRules[self.rawValue] = recursiveRule
// Create the rule we would normally generate
let rule = [ "recurse ", T.recursiveItem.rule].sequence.reference(.structural(token: self), annotations: [RuleAnnotation.custom(label:"recursive"):RuleAnnotationValue.bool(true)])

recursiveRule.surrogateRule = rule
return recursiveRule
}

return cachedRule

/// recursiveItem
case .recursiveItem:
guard let cachedRule = T.leftHandRecursiveRules[self.rawValue] else {
// Create recursive shell
let recursiveRule = RecursiveRule(stubFor: Behaviour(.structural(token: self), cardinality: Cardinality.one), with: [:])
T.leftHandRecursiveRules[self.rawValue] = recursiveRule
// Create the rule we would normally generate
let rule = T.recursive.rule.reference(.scanning)

recursiveRule.surrogateRule = rule
return recursiveRule
}

return cachedRule

/// normal
case .normal:
return [ T.terminal.rule, T.group.rule, T.identifier.rule, T.recursive.rule].sequence.reference(.structural(token: self))

/// overrides
case .overrides:
return [ T.terminal.rule.annotatedWith([RuleAnnotation.custom(label:"terminal"):RuleAnnotationValue.bool(false)]), T.group.rule.annotatedWith([RuleAnnotation.custom(label:"group"):RuleAnnotationValue.bool(false)]), T.identifier.rule.annotatedWith([RuleAnnotation.custom(label:"identifier"):RuleAnnotationValue.bool(false)]), T.recursive.rule.annotatedWith([RuleAnnotation.custom(label:"recursive"):RuleAnnotationValue.bool(false)])].sequence.reference(.structural(token: self))

}
}

/// Cache for left-hand recursive rules
private static var leftHandRecursiveRules = [ Int : Rule ]()

/// Create a language that can be used for parsing etc
public static var generatedRules: [Rule] {
return [T.normal.rule, T.overrides.rule]
}
}

public struct AnnotationTest : Codable {

/// Group
public struct Group : Codable {
public let terminal: Swift.String
}

/// Identifier
public struct Identifier : Codable {
public let terminal: Swift.String
}

/// Normal
public struct Normal : Codable {
public let group: Group
public let identifier: Identifier
public let recursive: Swift.String
public let terminal: Swift.String
}

/// Overrides
public struct Overrides : Codable {
public let group: Group
public let identifier: Identifier
public let recursive: Swift.String
public let terminal: Swift.String
}
public let normal : Normal
public let overrides : Overrides
/**
Parses the supplied string using the generated grammar into a new instance of
the generated data structure

- Parameter source: The string to parse
- Returns: A new instance of the data-structure
*/
public static func build(_ source : Swift.String) throws ->AnnotationTest{
let root = HomogenousTree(with: StringToken("root"), matching: source, children: [try AbstractSyntaxTreeConstructor().build(source, using: AnnotationTest.generatedLanguage)])
// print(root.description)
return try ParsingDecoder().decode(AnnotationTest.self, using: root)
}

public static var generatedLanguage : Grammar {return AnnotationTestTokens.generatedRules}
}
File renamed without changes.
2 changes: 1 addition & 1 deletion Tests/OysterKitTests/FixValidations.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import XCTest
@testable import OysterKit
@testable import ExampleLanguages
@testable import TestingSupport

fileprivate enum STLRStringTest : Int, TokenType {

Expand Down
55 changes: 55 additions & 0 deletions Tests/OysterKitTests/RuleAnnotationDescriptionTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,61 @@ class RuleAnnotationDescriptionTests: XCTestCase {

var ruleDict: [RuleAnnotation: RuleAnnotationValue]?

// MARK: - should sort standard entries

func testStandardEntrySort() {
ruleDict = [
.token: .string("fooBar"),
.error: .string("some error"),
.void: .string("looks at you"),
.transient: .set,
.pinned: .int(56),
.type: .bool(true)
]
let desc = "@void(\"looks at you\") @transient @token(\"fooBar\") " +
"@pin(56) @type(true) @error(\"some error\")"
XCTAssertEqual(desc, ruleDict?.description)
}

func testCustomEntryPushToBackSort() {
ruleDict = [
.custom(label: "foo"): .int(42),
.token: .string("fooBar"),
.error: .string("some error"),
.void: .string("looks at you"),
.transient: .set,
.pinned: .int(33),
.type: .bool(true)
]
let desc = "@void(\"looks at you\") @transient @token(\"fooBar\") " +
"@pin(33) @type(true) @error(\"some error\") @foo(42)"
XCTAssertEqual(desc, ruleDict?.description)
}

func testAlphabeticalCustomEntrySort() {
ruleDict = [
.custom(label: "citrus"): .set,
.custom(label: "animal"): .string("lion"),
.custom(label: "bee"): .bool(true)
]
let desc = "@animal(\"lion\") @bee(true) @citrus"
XCTAssertEqual(desc, ruleDict?.description)
}

func testFewStandardAndMultipleCustomEntrySort() {
ruleDict = [
.custom(label: "pie"): .string("apple"),
.custom(label: "eye"): .set,
.custom(label: "like"): .bool(true),
.token: .string("fooBar"),
.void: .string("looks at you"),
.pinned: .int(33)
]
let desc = "@void(\"looks at you\") @token(\"fooBar\") @pin(33) " +
"@eye @like(true) @pie(\"apple\")"
XCTAssertEqual(desc, ruleDict?.description)
}

// MARK: - @token tests

func testTokenString() {
Expand Down
Loading