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

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