AST implementation

This commit is contained in:
2026-01-26 21:37:49 -05:00
parent 19e4ad9bfe
commit afe33e4246
12 changed files with 147 additions and 67 deletions

View File

@@ -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 }

BIN
bin/a.out Executable file

Binary file not shown.

View File

@@ -1,4 +0,0 @@
.globl main
main:
movl $12, %eax
ret

View File

@@ -1,4 +0,0 @@
.globl main
main:
movl $5, %eax
ret

View File

@@ -1,4 +0,0 @@
.globl main
main:
movl $100, %eax
ret

View File

@@ -1,4 +0,0 @@
.globl main
main:
movl $0, %eax
ret

View File

@@ -1,4 +0,0 @@
.globl main
main:
movl $0, %eax
ret

View File

@@ -1,4 +0,0 @@
.globl main
main:
movl $0, %eax
ret

View File

@@ -1,4 +0,0 @@
.globl main
main:
movl $2, %eax
ret

View File

@@ -1,4 +0,0 @@
.globl main
main:
movl $0, %eax
ret