AST implementation
This commit is contained in:
@@ -17,8 +17,6 @@ enum TokenType {
|
||||
case UNDEFINED
|
||||
}
|
||||
|
||||
let UNARY_OPERATORS: [TokenType] = [.NEGATION, .BITWISE_COMPLIMENT, .LOGICAL_NEGATION]
|
||||
|
||||
struct Token {
|
||||
let content: Substring
|
||||
let type: TokenType
|
||||
@@ -28,7 +26,7 @@ typealias Construct = [Element]
|
||||
|
||||
enum Element {
|
||||
case Construct(type: ConstructDefinitions);
|
||||
case Token(types: [TokenType])
|
||||
case Token(type: TokenType)
|
||||
}
|
||||
|
||||
enum ConstructType {
|
||||
@@ -38,20 +36,95 @@ enum ConstructType {
|
||||
case Program
|
||||
}
|
||||
|
||||
enum ConstructVariant {
|
||||
// Expression variants
|
||||
case LiteralInteger
|
||||
case Negation
|
||||
case BitwiseCompliment
|
||||
case LogicalNegation
|
||||
|
||||
// Statement variants
|
||||
case ReturnInteger
|
||||
|
||||
// Function Variants
|
||||
case Integer
|
||||
|
||||
// Program Variants
|
||||
case SingleFunction
|
||||
|
||||
// Misc.
|
||||
case Root
|
||||
case Error
|
||||
}
|
||||
|
||||
class SyntaxTreeNode {
|
||||
var parent: SyntaxTreeNode?
|
||||
var children: [SyntaxTreeNode]
|
||||
|
||||
var variant: ConstructVariant
|
||||
var value: String
|
||||
|
||||
init(_ variant: ConstructVariant) {
|
||||
self.parent = nil
|
||||
self.children = [SyntaxTreeNode]()
|
||||
self.variant = variant
|
||||
self.value = ""
|
||||
}
|
||||
|
||||
init(_ variant: ConstructVariant, parent: SyntaxTreeNode) {
|
||||
self.parent = parent
|
||||
self.children = [SyntaxTreeNode]()
|
||||
self.variant = variant
|
||||
self.value = ""
|
||||
}
|
||||
|
||||
func addChild(value: ConstructVariant) -> SyntaxTreeNode {
|
||||
let child = SyntaxTreeNode(value, parent: self)
|
||||
children.append(child)
|
||||
return child
|
||||
}
|
||||
|
||||
func popLastChild() -> SyntaxTreeNode {
|
||||
if let last: SyntaxTreeNode = children.popLast() {
|
||||
return last
|
||||
}
|
||||
return SyntaxTreeNode(.Error)
|
||||
}
|
||||
|
||||
func text(_ level: Int = 0) -> String {
|
||||
var text: String = "\(variant)"
|
||||
if variant == .LiteralInteger {
|
||||
text += "(\(value))"
|
||||
}
|
||||
for child in children {
|
||||
text += "\n\(String(repeating: " ", count: level))└───\(child.text(level + 1))"
|
||||
}
|
||||
return text
|
||||
}
|
||||
}
|
||||
|
||||
struct ConstructDefinitions {
|
||||
var type: ConstructType
|
||||
var variants: Dictionary<String, Construct>
|
||||
var variants: Dictionary<ConstructVariant, Construct>
|
||||
}
|
||||
|
||||
let expression: ConstructDefinitions = ConstructDefinitions(
|
||||
type: .Expression,
|
||||
variants: [
|
||||
"LiteralInteger": [
|
||||
.Token(types: [.LITERAL_INTEGER])
|
||||
.LiteralInteger: [
|
||||
.Token(type: .LITERAL_INTEGER)
|
||||
],
|
||||
"UnaryOperator": [
|
||||
.Token(types: [.NEGATION]),
|
||||
.Token(types: UNARY_OPERATORS)
|
||||
.Negation: [
|
||||
.Token(type: .NEGATION),
|
||||
.Token(type: .LITERAL_INTEGER)
|
||||
],
|
||||
.BitwiseCompliment: [
|
||||
.Token(type: .BITWISE_COMPLIMENT),
|
||||
.Token(type: .LITERAL_INTEGER)
|
||||
],
|
||||
.LogicalNegation: [
|
||||
.Token(type: .LOGICAL_NEGATION),
|
||||
.Token(type: .LITERAL_INTEGER)
|
||||
],
|
||||
]
|
||||
)
|
||||
@@ -59,10 +132,10 @@ let expression: ConstructDefinitions = ConstructDefinitions(
|
||||
let statement: ConstructDefinitions = ConstructDefinitions(
|
||||
type: .Statement,
|
||||
variants: [
|
||||
"ReturnInteger": [
|
||||
.Token(types: [.RETURN]),
|
||||
.ReturnInteger: [
|
||||
.Token(type: .RETURN),
|
||||
.Construct(type: expression),
|
||||
.Token(types: [.SEMICOLON])
|
||||
.Token(type: .SEMICOLON)
|
||||
]
|
||||
]
|
||||
)
|
||||
@@ -70,14 +143,14 @@ let statement: ConstructDefinitions = ConstructDefinitions(
|
||||
let function: ConstructDefinitions = ConstructDefinitions(
|
||||
type: .Function,
|
||||
variants: [
|
||||
"Integer": [
|
||||
.Token(types: [.INT]),
|
||||
.Token(types: [.IDENTIFIER]),
|
||||
.Token(types: [.PARENTHESIS_OPEN]),
|
||||
.Token(types: [.PARENTHESIS_CLOSE]),
|
||||
.Token(types: [.BRACE_OPEN]),
|
||||
.Integer: [
|
||||
.Token(type: .INT),
|
||||
.Token(type: .IDENTIFIER),
|
||||
.Token(type: .PARENTHESIS_OPEN),
|
||||
.Token(type: .PARENTHESIS_CLOSE),
|
||||
.Token(type: .BRACE_OPEN),
|
||||
.Construct(type: statement),
|
||||
.Token(types: [.BRACE_CLOSE])
|
||||
.Token(type: .BRACE_CLOSE)
|
||||
]
|
||||
]
|
||||
)
|
||||
@@ -85,7 +158,7 @@ let function: ConstructDefinitions = ConstructDefinitions(
|
||||
let program: ConstructDefinitions = ConstructDefinitions(
|
||||
type: .Function,
|
||||
variants: [
|
||||
"Function": [
|
||||
.SingleFunction: [
|
||||
.Construct(type: function)
|
||||
]
|
||||
]
|
||||
@@ -154,7 +227,7 @@ func getTestFiles() -> [TestFile] {
|
||||
var testFiles: [TestFile] = [TestFile]()
|
||||
|
||||
let fileManager = FileManager.default
|
||||
let path = "c/tests/stage_2"
|
||||
let path = "c/tests/stage_1"
|
||||
|
||||
do {
|
||||
let validItems = try fileManager.contentsOfDirectory(atPath: path + "/valid")
|
||||
@@ -215,33 +288,62 @@ func parse(lexed: [Substring]) -> String {
|
||||
}
|
||||
tokens = tokens.reversed()
|
||||
|
||||
var output: String = ""
|
||||
let abstractSyntaxTree = SyntaxTreeNode(.Root)
|
||||
|
||||
if validateConstruct(program.variants["Function"]!, constructType: .Function, tokens: &tokens, output: &output) {
|
||||
if validateConstruct(program.variants[.SingleFunction]!, tokens: &tokens, node: abstractSyntaxTree) {
|
||||
print("Success")
|
||||
print(abstractSyntaxTree.text())
|
||||
print("Assembly:")
|
||||
print(output)
|
||||
print()
|
||||
|
||||
return output
|
||||
let assembly: String = generateOutput(tree: abstractSyntaxTree)
|
||||
print(assembly)
|
||||
return assembly
|
||||
}
|
||||
|
||||
print("Distinct lack of success")
|
||||
return ""
|
||||
}
|
||||
|
||||
func validateConstruct(_ construct: Construct, constructType: ConstructType, tokens: inout [Token], output: inout String) -> Bool {
|
||||
func generateOutput(tree: SyntaxTreeNode) -> String {
|
||||
var text: String = ""
|
||||
switch tree.variant {
|
||||
case .Integer:
|
||||
text += " .globl \(tree.value)\n\(tree.value):\n"
|
||||
break
|
||||
|
||||
case .ReturnInteger:
|
||||
text += " movl $\(tree.children[0].value), %eax\n ret\n"
|
||||
break
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
for child in tree.children {
|
||||
text += generateOutput(tree: child)
|
||||
}
|
||||
|
||||
return text
|
||||
}
|
||||
|
||||
func validateConstruct(_ construct: Construct, tokens: inout [Token], node: SyntaxTreeNode) -> Bool {
|
||||
for element in construct {
|
||||
switch element {
|
||||
case .Construct(let type):
|
||||
print("Begin validate subconstruct (type \"\(type.type)\")")
|
||||
|
||||
var valid: Bool = false
|
||||
var validVariant: String = ""
|
||||
var validVariant: ConstructVariant = .Error
|
||||
let tokenBackup: [Token] = tokens
|
||||
for key in type.variants.keys {
|
||||
if !validateConstruct(type.variants[key]!, constructType: type.type, tokens: &tokens, output: &output) {
|
||||
let childNode = node.addChild(value: key)
|
||||
print("Testing variant \(key)")
|
||||
if !validateConstruct(type.variants[key]!, tokens: &tokens, node: childNode) {
|
||||
print("Fail")
|
||||
tokens = tokenBackup
|
||||
_ = node.popLastChild()
|
||||
continue
|
||||
}
|
||||
print("Success")
|
||||
valid = true
|
||||
validVariant = key
|
||||
break
|
||||
@@ -251,16 +353,26 @@ func validateConstruct(_ construct: Construct, constructType: ConstructType, tok
|
||||
return false
|
||||
}
|
||||
print("End validate subconstruct (variant \"\(validVariant)\")")
|
||||
break
|
||||
return true
|
||||
|
||||
case .Token(let type):
|
||||
if let token: Token = tokens.popLast() {
|
||||
if !type.contains(token.type) {
|
||||
if type != token.type {
|
||||
print("VALIDATION FAILED FOR TOKEN \"\(token.content)\"")
|
||||
return false
|
||||
}
|
||||
print("Validated token \"\(token.content)\"")
|
||||
|
||||
switch constructType {
|
||||
if token.type == .LITERAL_INTEGER {
|
||||
node.value = String(token.content)
|
||||
}
|
||||
else if token.type == .IDENTIFIER {
|
||||
node.value = String(token.content)
|
||||
}
|
||||
|
||||
continue
|
||||
|
||||
/*switch constructType {
|
||||
case .Function:
|
||||
if token.type == .IDENTIFIER {
|
||||
output.append(" .globl \(token.content)\n\(token.content):\n")
|
||||
@@ -273,7 +385,7 @@ func validateConstruct(_ construct: Construct, constructType: ConstructType, tok
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
}*/
|
||||
|
||||
} else {
|
||||
print("RAN OUT OF TOKENS")
|
||||
@@ -294,7 +406,7 @@ func categorizeToken(token: Substring) -> TokenType {
|
||||
else if token.firstMatch(of: /^int$/) != nil { return .INT }
|
||||
else if token.firstMatch(of: /^return$/) != nil { return .RETURN }
|
||||
else if token.firstMatch(of: /^[a-zA-Z]\w*$/) != nil { return .IDENTIFIER }
|
||||
else if token.firstMatch(of: /^[0-9]+$/) != nil { return .LITERAL_INTEGER }
|
||||
else if token.firstMatch(of: /^[0-9]+$/) != nil { return .LITERAL_INTEGER}
|
||||
else if token.firstMatch(of: /^-$/) != nil { return .NEGATION }
|
||||
else if token.firstMatch(of: /^~$/) != nil { return .BITWISE_COMPLIMENT }
|
||||
else if token.firstMatch(of: /^!$/) != nil { return .LOGICAL_NEGATION }
|
||||
|
||||
Reference in New Issue
Block a user