Skip to content

Instantly share code, notes, and snippets.

@Stiivi
Last active January 11, 2016 03:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Stiivi/7745f18b382628bb9db8 to your computer and use it in GitHub Desktop.
Save Stiivi/7745f18b382628bb9db8 to your computer and use it in GitHub Desktop.
//: Playground - noun: a place where people can play
// Note: we are using underscode instead of camelCase for gramar rules
// to maintain consistency with common practice in BNF
import ParserCombinator
extension Token: EmptyCheckable {
public static let EmptyValue = Token(.Empty, "")
public var isEmpty: Bool { return self.kind == TokenKind.Empty }
}
enum SymbolType: CustomStringConvertible {
case Tag
case Slot
case Counter
case Concept
var description: String {
switch self {
case .Tag: return "tag"
case .Slot: return "slot"
case .Counter: return "counter"
case .Concept: return "concept"
}
}
}
/// Partial AST – wraps only nodes that might not have immediate object
/// representation. For example elements of mixed-object lists.
// TODO: break this into smaller pieces, instead of this too generic wrapper
indirect enum AST {
case Tags(TagList)
case Slots([Symbol])
case Counter(Symbol, Int)
case Modifier(String, AST)
case Selector(String, [AST])
case Predicate(String, AST)
case ASTConcept(Concept)
case ASTActuator(Actuator)
}
// Terminals
// =======================================================================
func token(kind: TokenKind, _ expected: String) -> Parser<Token, Token> {
return satisfy(expected) { token in token == kind }
}
func tokenValue(kind: TokenKind, _ value: String) -> Parser<Token, Token> {
return satisfy(value) { token in token == Token(kind, value) }
}
let symbol = { name in token(.Identifier, name) => { t in Symbol(t.text) } }
let number = { label in token(.IntLiteral, label) => { t in Int(t.text)! } }
let keyword = { kw in tokenValue(.Keyword, kw) => { t in t.text } }
let op = { o in tokenValue(.Operator, o) }
// TODO: This is just to type-hint the following rule. Otherwise Swift does not
// know that the `type` is of `SymbolType`
func typeDesc(type: SymbolType) -> String {
return type.description
}
// Primitive Items
// =======================================================================
let symbol_list = { type in separated(symbol(typeDesc(type)), op(",")) }
let tag_list = symbol_list(.Tag) => { ast in TagList(ast) }
// String comparable
public prefix func §(value: String) -> Parser<Token, String>{
return keyword(value)
}
public prefix func %(value: String) -> Parser<Token, Symbol>{
return symbol(value)
}
infix operator ... { associativity left precedence 130 }
public func ...<T, A, B>(p: Parser<T,A>, sep:Parser<T,B>) -> Parser<T,[A]> {
return separated(p, sep)
}
// TODO: There must be a nicer way...
func makeConcept(name: String, _ members: [AST]) -> Concept {
let allTags: [TagList] = members.flatMap {
switch $0 {
case .Tags(let syms): return syms
default: return nil
}
}
let allSlots: [[Symbol]] = members.flatMap {
switch $0 {
case .Slots(let syms): return syms
default: return nil
}
}
let allCounters: [(String, Int)] = members.flatMap {
switch $0 {
case .Counter(let sym, let count): return (sym, count)
default: return nil
}
}
let tags = TagList(allTags.flatten())
let slots = [Symbol](allSlots.flatten())
let counters = CounterDict(items: allCounters)
return Concept(name: name, tags: tags, slots: slots, counters: counters)
}
// Concept
// =======================================================================
let concept_member =
§"TAG" *> tag_list => { tags in AST.Tags(tags) }
|| §"SLOT" *> symbol_list(.Slot) => { syms in AST.Slots(syms) }
|| §"COUNTER" *> %"counter" + ((§"=" *> number("initial count")) || succeed(0))
=> { (sym, count) in return AST.Counter(sym, count)}
let concept =
§"CONCEPT" *> %"name" + many(concept_member) => { (name, members) in makeConcept(name, members) }
let predicate_type =
§"SET" *> tag_list => { tags in PredicateType.TagSet(tags) }
|| §"BOUND" *> %"slot" => { symbol in PredicateType.CounterZero(symbol) }
|| §"ZERO" *> %"counter" => { symbol in PredicateType.IsBound(symbol)}
// Actuator
// ========================================================================
// Selector
// ------------------------------------------------------------------------
// §"NOT" *> succeed(true) || succeed(false)
let predicate =
(optionFlag(§"NOT") + option(§"IN" *> %"slot") + predicate_type)
=> { (ctx, type) in return Predicate(type, ctx.0, inSlot: ctx.1) }
let predicate_list = (predicate ... §"AND")
let selector =
§"ALL" => { _ in Selector.All }
|| §"ROOT" *> (predicate ... §"AND") => { Selector.Filter($0) }
|| (predicate ... §"AND") => { Selector.Root($0) }
// Modifier
// ------------------------------------------------------------------------
let target_type =
§"THIS" *> succeed(TargetType.This)
|| §"OTHER" *> succeed(TargetType.Other)
|| §"ROOT" *> succeed(TargetType.Root)
|| succeed(TargetType.This)
let modifier_target =
target_type + option(%"slot") => { target in ModifierTarget(target.0, target.1) }
let bind_target =
modifier_target
|| %"slot" => { symbol in ModifierTarget(TargetType.This, symbol)}
let modifier_action =
§"NOTHING" => { _ in ModifierAction.Nothing}
|| §"SET" *> tag_list => { tags in ModifierAction.SetTags(tags)}
|| §"UNSET" *> tag_list => { tags in ModifierAction.UnsetTags(tags)}
|| §"INC" *> %"counter" => { symbol in ModifierAction.Inc(symbol)}
|| §"DEC" *> %"counter" => { symbol in ModifierAction.Dec(symbol)}
|| §"CLEAR" *> %"counter" => { symbol in ModifierAction.Clear(symbol)}
|| §"UNBIND" *> %"slot" => { symbol in ModifierAction.Unbind(symbol)}
|| §"BIND" *> %"slot" + (§"TO" *> bind_target) => { ast in ModifierAction.Bind(ast.1, ast.0)}
let modifier =
((§"IN" *> modifier_target) || succeed(ModifierTarget(.This) ))
+ modifier_action => { mod in Modifier(target: mod.0, action: mod.1) }
// WHERE something DO something
// WHERE something ON somethin else DO something
// ((selector, selector?), [modifier])
let actuator =
((§"WHERE" *> selector) + option(§"ON" *> selector)) + (§"DO" *> some(modifier))
=> { ast in Actuator(selector: ast.0.0, combinedSelector:ast.0.1, modifiers:ast.1) }
// AST
let model_object =
concept => { ast in AST.ASTConcept(ast) }
|| actuator => { ast in AST.ASTActuator(ast) }
let model = many(model_object)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment