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