Files
rxcc/Sources/rxcc/rxcc.swift
2026-02-13 20:02:36 -05:00

1682 lines
57 KiB
Swift

import Foundation
@main
struct rxcc {
var variables: Dictionary<String, Int> = [:]
var stackIndex: Int = 0
static func main() {
var compiler = rxcc()
compiler.initialize()
}
mutating func initialize() {
if CommandLine.arguments.count < 2 {
for testFile in getTestFiles() {
print("Testing \(testFile.valid ? "valid" : "invalid") file \(testFile.name):")
print(testFile.contents)
variables = [:]
stackIndex = 0
let lexed: [Substring] = lex(string: testFile.contents)
let output: String = parse(lexed: lexed)
if output.isEmpty {
print()
continue
}
do {
try output.write(to: URL(fileURLWithPath: "bin").appendingPathComponent("\(testFile.name.dropLast())s"), atomically: true, encoding: .utf8)
} catch {
print("Couldn't write file \"\(testFile.name.dropLast())s\"")
}
print()
}
} else {
var text = ""
do {
text = try String(contentsOf: URL(fileURLWithPath: CommandLine.arguments[1]), encoding: .utf8)
} catch {
print("Error reading file from command line argument")
}
print(CommandLine.arguments)
let lexed: [Substring] = lex(string: text)
let output: String = parse(lexed: lexed)
if output.isEmpty {
print("Failed to compile")
}
var fileName = String(CommandLine.arguments[1].dropLast() + "s")
fileName = URL(fileURLWithPath: fileName).lastPathComponent
do {
try output.write(to: URL(fileURLWithPath: fileName), atomically: true, encoding: .utf8)
} catch {
print("Couldn't write file \"\(CommandLine.arguments[1].dropLast())s\"")
}
}
}
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))"
} else if variant == .Variable {
text += "(\(value))"
}
for child in children {
text += "\n\(String(repeating: " ", count: level))└───\(child.text(level + 1))"
}
return text
}
}
enum TokenType {
case BRACE_OPEN
case BRACE_CLOSE
case PARENTHESIS_OPEN
case PARENTHESIS_CLOSE
case SEMICOLON
case INT
case RETURN
case IDENTIFIER
case LITERAL_INTEGER
case NEGATION
case BITWISE_COMPLIMENT
case LOGICAL_NEGATION
case ADDITION
case MULTIPLICATION
case DIVISION
case LOGICAL_AND
case LOGICAL_OR
case EQUAL
case NOT_EQUAL
case LESS_THAN
case LESS_THAN_OR_EQUAL_TO
case GREATER_THAN
case GREATER_THAN_OR_EQUAL_TO
case MODULO
case BITWISE_AND
case BITWISE_OR
case BITWISE_XOR
case BITWISE_SHIFT_LEFT
case BITWISE_SHIFT_RIGHT
case ASSIGNMENT
case COMPOUND_ASSIGNMENT_ADDITION
case COMPOUND_ASSIGNMENT_SUBTRACTION
case COMPOUND_ASSIGNMENT_MULTIPLICATION
case COMPOUND_ASSIGNMENT_DIVISION
case COMPOUND_ASSIGNMENT_MODULO
case COMPOUND_ASSIGNMENT_BITWISE_SHIFT_LEFT
case COMPOUND_ASSIGNMENT_BITWISE_SHIFT_RIGHT
case COMPOUND_ASSIGNMENT_BITWISE_AND
case COMPOUND_ASSIGNMENT_BITWISE_OR
case COMPOUND_ASSIGNMENT_BITWISE_XOR
case UNDEFINED
}
struct Token {
let content: Substring
let type: TokenType
}
typealias Construct = [Element]
enum Element {
case List(type: ConstructType)
case OperatorLoop(type: ConstructType)
case Construct(type: ConstructType)
case Optional(type: TokenType)
case Token(type: TokenType)
}
enum ConstructVariant {
case Variable
case StandaloneVariable
case StandardAssignment
case CompoundAssignmentAddition
case CompoundAssignmentSubtraction
case CompoundAssignmentMultiplication
case CompoundAssignmentDivision
case CompoundAssignmentModulo
case CompoundAssignmentBitwiseShiftLeft
case CompoundAssignmentBitwiseShiftRight
case CompoundAssignmentBitwiseAnd
case CompoundAssignmentBitwiseOr
case CompoundAssignmentBitwiseXOR
case Addition
case Subtraction
case Multiplication
case Division
case Modulo
case BitwiseShiftLeft
case BitwiseShiftRight
case LessThan
case LessThanOrEqualTo
case GreaterThan
case GreaterThanOrEqualTo
case EqualTo
case NotEqualTo
case BitwiseAnd
case BitwiseXOR
case BitwiseOr
case LogicalAnd
case LogicalOr
// Unary operation variants
case Negation
case BitwiseCompliment
case LogicalNegation
// Expression variants
case TermSequence
case AdditiveExpressionSequence
case BitwiseShiftExpressionSequence
case RelationalExpressionSequence
case EqualityExpressionSequence
case BitwiseAndExpressionSequence
case BitwiseXORExpressionSequence
case BitwiseOrExpressionSequence
case LogicalAndExpressionSequence
case LogicalOrExpressionSequence
case VariableExpression
// Term variants
case FactorSequence
// Factor variants
case LiteralInteger
case UnaryOperation
case ParenthesizedExpression
// Statement variants
case StatementSequence
case ReturnExpression
case DeclareVariable
case StandaloneExpression
// Function Variants
case Integer
// Program Variants
case SingleFunction
// Misc.
case Root
case Error
}
enum ConstructType {
case VariableFactor
case VariableDeclaration
case VariableAssignment
case UnaryOperator
case MultiplicationPriorityOperator
case AdditionPriorityOperator
case BitwiseShiftPriorityOperator
case InequalityPriorityOperator
case EqualityPriorityOperator
case BitwiseAndPriorityOperator
case BitwiseXORPriorityOperator
case BitwiseOrPriorityOperator
case LogicalAndPriorityOperator
case LogicalOrPriorityOperator
case AssignmentOperator
case Program
case Function
case StatementList
case Statement
case Expression
case LogicalOrExpression
case LogicalAndExpression
case BitwiseOrExpression
case BitwiseXORExpression
case BitwiseAndExpression
case EqualityExpression
case RelationalExpression
case BitwiseShiftExpression
case AdditiveExpression
case Term
case Factor
}
struct ConstructDefinitions {
var type: ConstructType
var variants: Dictionary<ConstructVariant, Construct>
}
let constructDefinitions: Dictionary<ConstructType, Dictionary<ConstructVariant, Construct>> = [
.VariableFactor: [
.Variable: [
.Token(type: .IDENTIFIER),
],
],
.VariableDeclaration: [
.Variable: [
.Token(type: .INT),
.Token(type: .IDENTIFIER),
.Optional(type: .ASSIGNMENT),
.Construct(type: .Expression),
],
],
.VariableAssignment: [
.Variable: [
.Token(type: .IDENTIFIER),
.Construct(type: .AssignmentOperator),
.Construct(type: .Expression),
],
],
.AssignmentOperator: [
.StandardAssignment: [
.Token(type: .ASSIGNMENT),
],
.CompoundAssignmentAddition: [
.Token(type: .COMPOUND_ASSIGNMENT_ADDITION),
],
.CompoundAssignmentSubtraction: [
.Token(type: .COMPOUND_ASSIGNMENT_SUBTRACTION),
],
.CompoundAssignmentMultiplication: [
.Token(type: .COMPOUND_ASSIGNMENT_MULTIPLICATION),
],
.CompoundAssignmentDivision: [
.Token(type: .COMPOUND_ASSIGNMENT_DIVISION),
],
.CompoundAssignmentModulo: [
.Token(type: .COMPOUND_ASSIGNMENT_MODULO),
],
.CompoundAssignmentBitwiseShiftLeft: [
.Token(type: .COMPOUND_ASSIGNMENT_BITWISE_SHIFT_LEFT),
],
.CompoundAssignmentBitwiseShiftRight: [
.Token(type: .COMPOUND_ASSIGNMENT_BITWISE_SHIFT_RIGHT),
],
.CompoundAssignmentBitwiseAnd: [
.Token(type: .COMPOUND_ASSIGNMENT_BITWISE_AND),
],
.CompoundAssignmentBitwiseOr: [
.Token(type: .COMPOUND_ASSIGNMENT_BITWISE_OR),
],
.CompoundAssignmentBitwiseXOR: [
.Token(type: .COMPOUND_ASSIGNMENT_BITWISE_XOR),
],
],
.UnaryOperator: [
.Negation: [
.Token(type: .NEGATION),
],
.BitwiseCompliment: [
.Token(type: .BITWISE_COMPLIMENT),
],
.LogicalNegation: [
.Token(type: .LOGICAL_NEGATION),
],
],
.AdditionPriorityOperator: [
.Addition: [
.Token(type: .ADDITION),
],
.Subtraction: [
.Token(type: .NEGATION),
],
],
.MultiplicationPriorityOperator: [
.Multiplication: [
.Token(type: .MULTIPLICATION),
],
.Division: [
.Token(type: .DIVISION),
],
.Modulo: [
.Token(type: .MODULO),
],
],
.BitwiseShiftPriorityOperator: [
.BitwiseShiftLeft: [
.Token(type: .BITWISE_SHIFT_LEFT),
],
.BitwiseShiftRight: [
.Token(type: .BITWISE_SHIFT_RIGHT),
],
],
.InequalityPriorityOperator: [
.LessThan: [
.Token(type: .LESS_THAN),
],
.LessThanOrEqualTo: [
.Token(type: .LESS_THAN_OR_EQUAL_TO),
],
.GreaterThan: [
.Token(type: .GREATER_THAN),
],
.GreaterThanOrEqualTo: [
.Token(type: .GREATER_THAN_OR_EQUAL_TO),
],
],
.EqualityPriorityOperator: [
.EqualTo: [
.Token(type: .EQUAL),
],
.NotEqualTo: [
.Token(type: .NOT_EQUAL),
],
],
.BitwiseAndPriorityOperator: [
.BitwiseAnd: [
.Token(type: .BITWISE_AND),
],
],
.BitwiseXORPriorityOperator: [
.BitwiseXOR: [
.Token(type: .BITWISE_XOR),
],
],
.BitwiseOrPriorityOperator: [
.BitwiseOr: [
.Token(type: .BITWISE_OR),
],
],
.LogicalAndPriorityOperator: [
.LogicalAnd: [
.Token(type: .LOGICAL_AND),
],
],
.LogicalOrPriorityOperator: [
.LogicalOr: [
.Token(type: .LOGICAL_OR),
],
],
// Expressions
.Expression: [
.VariableExpression: [
.Construct(type: .VariableAssignment)
],
.LogicalOrExpressionSequence: [
.Construct(type: .LogicalOrExpression)
],
],
.LogicalOrExpression: [
.LogicalAndExpressionSequence: [
.Construct(type: .LogicalAndExpression),
.OperatorLoop(type: .LogicalOrPriorityOperator),
]
],
.LogicalAndExpression: [
.BitwiseOrExpressionSequence: [
.Construct(type: .BitwiseOrExpression),
.OperatorLoop(type: .LogicalAndPriorityOperator),
]
],
//////////////////////////////////////////////////////
.BitwiseOrExpression: [
.BitwiseXORExpressionSequence: [
.Construct(type: .BitwiseXORExpression),
.OperatorLoop(type: .BitwiseOrPriorityOperator)
]
],
.BitwiseXORExpression: [
.BitwiseAndExpressionSequence: [
.Construct(type: .BitwiseAndExpression),
.OperatorLoop(type: .BitwiseXORPriorityOperator)
]
],
.BitwiseAndExpression: [
.EqualityExpressionSequence: [
.Construct(type: .EqualityExpression),
.OperatorLoop(type: .BitwiseAndPriorityOperator)
]
],
//////////////////////////////////////////////////////
.EqualityExpression: [
.RelationalExpressionSequence: [
.Construct(type: .RelationalExpression),
.OperatorLoop(type: .EqualityPriorityOperator),
]
],
.RelationalExpression: [
.BitwiseShiftExpressionSequence: [
.Construct(type: .BitwiseShiftExpression),
.OperatorLoop(type: .InequalityPriorityOperator),
]
],
.BitwiseShiftExpression: [
.AdditiveExpressionSequence: [
.Construct(type: .AdditiveExpression),
.OperatorLoop(type: .BitwiseShiftPriorityOperator),
]
],
.AdditiveExpression: [
.TermSequence: [
.Construct(type: .Term),
.OperatorLoop(type: .AdditionPriorityOperator),
]
],
// Sub-expressions
.Term: [
.FactorSequence: [
.Construct(type: .Factor),
.OperatorLoop(type: .MultiplicationPriorityOperator),
]
],
.Factor: [
.LiteralInteger: [
.Token(type: .LITERAL_INTEGER)
],
.UnaryOperation: [
.Construct(type: .UnaryOperator),
.Construct(type: .Factor)
],
.ParenthesizedExpression: [
.Token(type: .PARENTHESIS_OPEN),
.Construct(type: .Expression),
.Token(type: .PARENTHESIS_CLOSE)
],
.StandaloneVariable: [
.Construct(type: .VariableFactor)
]
],
// Super-expressions
.StatementList: [
.StatementSequence: [
.List(type: .Statement)
]
],
.Statement: [
.ReturnExpression: [
.Token(type: .RETURN),
.Construct(type: .Expression),
.Token(type: .SEMICOLON)
],
.StandaloneExpression: [
.Construct(type: .Expression),
.Token(type: .SEMICOLON)
],
.DeclareVariable: [
.Construct(type: .VariableDeclaration),
.Token(type: .SEMICOLON)
],
],
.Function: [
.Integer: [
.Token(type: .INT),
.Token(type: .IDENTIFIER),
.Token(type: .PARENTHESIS_OPEN),
.Token(type: .PARENTHESIS_CLOSE),
.Token(type: .BRACE_OPEN),
.Construct(type: .StatementList),
.Token(type: .BRACE_CLOSE)
]
],
.Program: [
.SingleFunction: [
.Construct(type: .Function)
]
]
]
let program: ConstructDefinitions = ConstructDefinitions(
type: .Function,
variants: [
.SingleFunction: [
.Construct(type: .Function)
]
]
)
struct TestFile {
var name: String
var path: String
var contents: String
var valid: Bool
}
func getTestFiles() -> [TestFile] {
var testFiles: [TestFile] = [TestFile]()
let fileManager = FileManager.default
let path = "c/tests/stage_5"
do {
let validItems = try fileManager.contentsOfDirectory(atPath: path + "/valid")
for item in validItems {
if item.last?.lowercased() != "c" {
continue
}
let fileURL = URL(fileURLWithPath: path + "/valid").appendingPathComponent(item)
let text = try String(contentsOf: fileURL, encoding: .utf8)
testFiles.append(TestFile(name: item, path: path + "/valid/", contents: text, valid: true))
}
let invalidItems = try fileManager.contentsOfDirectory(atPath: path + "/invalid")
for item in invalidItems {
if item.last?.lowercased() != "c" {
continue
}
let fileURL = URL(fileURLWithPath: path + "/invalid").appendingPathComponent(item)
let text = try String(contentsOf: fileURL, encoding: .utf8)
testFiles.append(TestFile(name: item, path: path + "/invalid/", contents: text, valid: false))
}
} catch {
print("You think I'm going to handle errors? You are mistaken.")
}
return testFiles
}
func lex(string: String) -> [Substring] {
var line = string.replacing("\n", with: " ")
line = line.replacing("\t", with: " ")
line = line.replacing("{", with: " { ")
line = line.replacing("}", with: " } ")
line = line.replacing("(", with: " ( ")
line = line.replacing(")", with: " ) ")
line = line.replacing(";", with: " ; ")
line = line.replacing("-", with: " - ")
line = line.replacing("~", with: " ~ ")
line = line.replacing("!", with: " ! ")
line = line.replacing("+", with: " + ")
line = line.replacing("*", with: " * ")
line = line.replacing("/", with: " / ")
line = line.replacing("<", with: " < ")
line = line.replacing(">", with: " > ")
line = line.replacing("=", with: " = ")
line = line.replacing("&", with: " & ")
line = line.replacing("|", with: " | ")
line = line.replacing("^", with: " ^ ")
line = line.replacing("&&", with: " && ")
line = line.replacing("||", with: " || ")
line = line.replacing("==", with: " == ")
line = line.replacing(/= +=/, with: " == ")
line = line.replacing(/! *=/, with: " != ")
line = line.replacing(/< *=/, with: " <= ")
line = line.replacing(/> *=/, with: " >= ")
line = line.replacing(/< *</, with: " << ")
line = line.replacing(/> *>/, with: " >> ")
line = line.replacing(/\+ *=/, with: " += ")
line = line.replacing(/- *=/, with: " -= ")
line = line.replacing(/\* *=/, with: " *= ")
line = line.replacing(/\/ *=/, with: " /= ")
line = line.replacing(/% *=/, with: " %= ")
line = line.replacing(/& *=/, with: " &= ")
line = line.replacing(/\| *=/, with: " |= ")
line = line.replacing(/\^ *=/, with: " ^= ")
line = line.replacing(/< *< *=/, with: " <<= ")
line = line.replacing(/> *> *=/, with: " >>= ")
do {
let tokens: [Substring] = line.split(separator: try Regex(" +"))
print("Tokens:")
print(tokens)
return tokens
} catch {
print("Regex did something wrong")
}
return []
}
mutating func generateOutput(_ node: SyntaxTreeNode) -> String {
var text: String = ""
switch node.variant {
case .Root:
for child in node.children {
text += generateOutput(child)
}
break
case .StatementSequence:
for child in node.children {
text += generateOutput(child)
}
break
case .ParenthesizedExpression:
for child in node.children {
text += generateOutput(child)
}
break
case .StandaloneVariable:
let count = node.children.count
if count != 1 {
print("ERROR: \(node.variant) with more or less than 1 child")
break
}
//text += generateOutput(node.children[0])
let variable: String = node.children[0].value
if let index = variables[variable] {
text += " movl\t\(index)(%ebp), %eax\n"
} else {
print("ERROR: \(variable) used before it was declared (in \(node.variant))")
}
break
case .DeclareVariable:
let count = node.children.count
if count != 1 {
print("ERROR: \(node.variant) with more or less than 1 child")
break
}
let variable: String = node.children[0].value
if variables.keys.contains(variable) {
print("ERROR: Redefinition of variable \"\(variable)\"")
break
}
variables.updateValue(stackIndex, forKey: variable)
stackIndex -= 4
let notInitialized: Bool = node.children[0].children.count == 0
//print("For variable \(variable), node children 0 children count == 0 : \(notInitialized)")
if notInitialized {
text += " push\t$0\n"
} else {
text += generateOutput(node.children[0])
text += " push\t%eax\n"
}
break
case .VariableExpression:
let count = node.children.count
if count != 1 {
print("ERROR: \(node.variant) with more or less than 1 child")
break
}
text += generateOutput(node.children[0])
text += " push\t%eax\n"
break
case .Variable:
let count = node.children.count
if count > 2 {
print("ERROR: \(node.variant) with more than 2 children")
break
}
let variable: String = node.value
if !variables.keys.contains(variable) {
print("ERROR: Variable \"\(variable)\" used before it was declared")
break
}
if count == 1 {
text += generateOutput(node.children[0]) // Get the value of the assignment
text += " movl\t%eax, \(variables[variable]!)(%ebp)\n"
break
}
if count != 0 {
text += generateOutput(node.children[1]) // Get the value of the assignment
}
switch node.children[0].variant {
case .StandardAssignment:
text += " movl\t%eax, \(variables[variable]!)(%ebp)\n"
break
case .CompoundAssignmentAddition:
text += " addl\t%eax, \(variables[variable]!)(%ebp)\n"
break
case .CompoundAssignmentSubtraction:
text += " subl\t%eax, \(variables[variable]!)(%ebp)\n"
break
case .CompoundAssignmentMultiplication:
text += " imul\t\(variables[variable]!)(%ebp), %eax\n"
text += " movl\t%eax, \(variables[variable]!)(%ebp)\n"
break
case .CompoundAssignmentDivision:
text += " movl\t%eax, %ecx\n" // Move e2 to ecx
text += " movl\t\(variables[variable]!)(%ebp), %eax\n" // Fetch e1 from variable into eax
text += " cdq\n" // Extend eax into edx
text += " idivl\t%ecx\n" // Perform edx:eax / ecx
text += " movl\t%eax, \(variables[variable]!)(%ebp)\n"
break
case .CompoundAssignmentModulo:
text += " movl\t%eax, %ecx\n" // Move e2 to ecx
text += " movl\t\(variables[variable]!)(%ebp), %eax\n" // Fetch e1 from variable into eax
text += " cdq\n" // Extend eax into edx
text += " idivl\t%ecx\n" // Perform edx:eax / ecx
text += " movl\t%edx, \(variables[variable]!)(%ebp)\n"
break
case .CompoundAssignmentBitwiseShiftLeft:
text += " movl\t%eax, %ecx\n" // Move e2 to ecx
text += " movl\t\(variables[variable]!)(%ebp), %eax\n" // Move e2 to ecx
text += " sal \t%cl, %eax\n" // Perform e1 << e2; result in e1
text += " movl\t%eax, \(variables[variable]!)(%ebp)\n" // Move e2 to ecx
break
case .CompoundAssignmentBitwiseShiftRight:
text += " movl\t%eax, %ecx\n" // Move e2 to ecx
text += " movl\t\(variables[variable]!)(%ebp), %eax\n" // Move e2 to ecx
text += " sar \t%cl, %eax\n" // Perform e1 << e2; result in e1
text += " movl\t%eax, \(variables[variable]!)(%ebp)\n" // Move e2 to ecx
break
case .CompoundAssignmentBitwiseAnd:
text += " and \t%eax, \(variables[variable]!)(%ebp)\n"
break
case .CompoundAssignmentBitwiseOr:
text += " or \t%eax, \(variables[variable]!)(%ebp)\n"
break
case .CompoundAssignmentBitwiseXOR:
text += " xor \t%eax, \(variables[variable]!)(%ebp)\n"
break
default:
print("ERROR: Unknown assignment type \"\(node.children[0].variant)\"")
break
}
break
case .LogicalOrExpressionSequence:
if node.children.count != 1 {
print("Error: \(node.variant) with more or less than one child")
}
text += generateOutput(node.children[0])
break
case .StandaloneExpression:
if node.children.count != 1 {
print("Error: \(node.variant) with more or less than one child")
}
text += generateOutput(node.children[0])
break
case .LogicalAndExpressionSequence:
var operation: ConstructVariant = .Error
let count = node.children.count
if count == 0 {
print("ERROR: \(node.variant) with no children")
break
}
for i in 0...count - 1 {
let child: SyntaxTreeNode = node.children[i]
let uuid: String = String(UUID().uuidString.split(separator: "-")[0])
// Operation
if i % 2 == 1 {
operation = child.variant
continue
}
text += generateOutput(child)
switch operation {
case .LogicalOr:
text += " movl\t%eax, %ecx\n" // Put e2 in ecx
text += " pop \t%eax\n" // Put e1 in eax
text += " cmpl\t$0, %eax\n"
text += " je \tlabel_\(uuid)\n"
text += " movl\t$1, %eax\n"
text += " jmp \tend_\(uuid)\n"
text += "label_\(uuid):\n"
text += " movl\t%ecx, %eax\n" // Put e2 in eax
text += " cmpl\t$0, %eax\n"
text += " movl\t$0, %eax\n"
text += " setne\t%al\n"
text += "end_\(uuid):\n"
default:
if i != 0 {
print("Unknown operation \"\(operation)\" in \(node.variant)")
}
break
}
if i != node.children.count - 1 {
text += " push\t%eax\n"
}
}
break
case .BitwiseOrExpressionSequence:
var operation: ConstructVariant = .Error
let count = node.children.count
if count == 0 {
print("ERROR: \(node.variant) with no children")
break
}
for i in 0...count - 1 {
let child: SyntaxTreeNode = node.children[i]
let uuid: String = String(UUID().uuidString.split(separator: "-")[0])
// Operation
if i % 2 == 1 {
operation = child.variant
continue
}
text += generateOutput(child)
switch operation {
case .LogicalAnd:
text += " movl\t%eax, %ecx\n" // Put e2 in ecx
text += " pop \t%eax\n" // Put e1 in eax
text += " cmpl\t$0, %eax\n"
text += " jne \tlabel_\(uuid)\n"
text += " jmp \tend_\(uuid)\n"
text += "label_\(uuid):\n"
text += " movl\t%ecx, %eax\n" // Put e2 in eax
text += " cmpl\t$0, %eax\n"
text += " movl\t$0, %eax\n"
text += " setne\t%al\n"
text += "end_\(uuid):\n"
default:
if i != 0 {
print("Unknown operation \"\(operation)\" in \(node.variant)")
}
break
}
if i != node.children.count - 1 {
text += " push\t%eax\n"
}
}
break
case .BitwiseXORExpressionSequence:
var operation: ConstructVariant = .Error
let count = node.children.count
if count == 0 {
print("ERROR: \(node.variant) with no children")
break
}
for i in 0...count - 1 {
let child: SyntaxTreeNode = node.children[i]
// Operation
if i % 2 == 1 {
operation = child.variant
continue
}
text += generateOutput(child)
switch operation {
case .BitwiseOr:
text += " pop \t%ecx\n" // Put e1 in ecx and e2 is in eax
text += " or \t%ecx, %eax\n" // Perform e1 | e2; result in eax
default:
if i != 0 {
print("Unknown operation \"\(operation)\" in \(node.variant)")
}
break
}
if i != node.children.count - 1 {
text += " push\t%eax\n"
}
}
break
case .BitwiseAndExpressionSequence:
var operation: ConstructVariant = .Error
let count = node.children.count
if count == 0 {
print("ERROR: \(node.variant) with no children")
break
}
for i in 0...count - 1 {
let child: SyntaxTreeNode = node.children[i]
// Operation
if i % 2 == 1 {
operation = child.variant
continue
}
text += generateOutput(child)
switch operation {
case .BitwiseXOR:
text += " pop \t%ecx\n" // Put e1 in ecx and e2 is in eax
text += " xor \t%ecx, %eax\n" // Perform e1 ^ e2; result in eax
default:
if i != 0 {
print("Unknown operation \"\(operation)\" in \(node.variant)")
}
break
}
if i != node.children.count - 1 {
text += " push\t%eax\n"
}
}
break
case .EqualityExpressionSequence:
var operation: ConstructVariant = .Error
let count = node.children.count
if count == 0 {
print("ERROR: \(node.variant) with no children")
break
}
for i in 0...count - 1 {
let child: SyntaxTreeNode = node.children[i]
// Operation
if i % 2 == 1 {
operation = child.variant
continue
}
text += generateOutput(child)
switch operation {
case .BitwiseAnd:
text += " pop \t%ecx\n" // Put e1 in ecx and e2 is in eax
text += " and \t%ecx, %eax\n" // Perform e1 & e2; result in eax
default:
if i != 0 {
print("Unknown operation \"\(operation)\" in \(node.variant)")
}
break
}
if i != node.children.count - 1 {
text += " push\t%eax\n"
}
}
break
case .RelationalExpressionSequence:
var operation: ConstructVariant = .Error
let count = node.children.count
if count == 0 {
print("ERROR: \(node.variant) with no children")
break
}
for i in 0...count - 1 {
let child: SyntaxTreeNode = node.children[i]
// Operation
if i % 2 == 1 {
operation = child.variant
continue
}
text += generateOutput(child)
switch operation {
case .EqualTo:
text += " pop \t%ecx\n"
text += " cmpl\t%eax, %ecx\n"
text += " movl\t$0, %eax\n"
text += " sete\t%al\n"
case .NotEqualTo:
text += " pop \t%ecx\n"
text += " cmpl\t%eax, %ecx\n"
text += " movl\t$0, %eax\n"
text += " setne\t%al\n"
default:
if i != 0 {
print("Unknown operation \"\(operation)\" in \(node.variant)")
}
break
}
if i != node.children.count - 1 {
text += " push\t%eax\n"
}
}
break
case .BitwiseShiftExpressionSequence:
var operation: ConstructVariant = .Error
let count = node.children.count
if count == 0 {
print("ERROR: \(node.variant) with no children")
break
}
for i in 0...count - 1 {
let child: SyntaxTreeNode = node.children[i]
// Operation
if i % 2 == 1 {
operation = child.variant
continue
}
text += generateOutput(child)
switch operation {
case .LessThan:
text += " pop \t%ecx\n"
text += " cmpl\t%eax, %ecx\n"
text += " movl\t$0, %eax\n"
text += " setl\t%al\n"
case .LessThanOrEqualTo:
text += " pop \t%ecx\n"
text += " cmpl\t%eax, %ecx\n"
text += " movl\t$0, %eax\n"
text += " setle\t%al\n"
case .GreaterThan:
text += " pop \t%ecx\n"
text += " cmpl\t%eax, %ecx\n"
text += " movl\t$0, %eax\n"
text += " setg\t%al\n"
case .GreaterThanOrEqualTo:
text += " pop \t%ecx\n"
text += " cmpl\t%eax, %ecx\n"
text += " movl\t$0, %eax\n"
text += " setge\t%al\n"
default:
if i != 0 {
print("Unknown operation \"\(operation)\" in \(node.variant)")
}
break
}
if i != node.children.count - 1 {
text += " push\t%eax\n"
}
}
break
case .AdditiveExpressionSequence:
var operation: ConstructVariant = .Error
let count = node.children.count
if count == 0 {
print("ERROR: \(node.variant) with no children")
break
}
for i in 0...count - 1 {
let child: SyntaxTreeNode = node.children[i]
// Operation
if i % 2 == 1 {
operation = child.variant
continue
}
text += generateOutput(child)
switch operation {
case .BitwiseShiftLeft:
text += " movl\t%eax, %ecx\n" // Move e2 to ecx
text += " pop \t%eax\n" // Move e1 to eax
text += " sal \t%cl, %eax\n" // Perform e1 << e2; result in e1
case .BitwiseShiftRight:
text += " movl\t%eax, %ecx\n" // Move e2 to ecx
text += " pop \t%eax\n" // Move e1 to eax
text += " sar \t%cl, %eax\n" // Perform e1 >> e2; result in e1
default:
if i != 0 {
print("Unknown operation \"\(operation)\" in \(node.variant)")
}
break
}
if i != node.children.count - 1 {
text += " push\t%eax\n"
}
}
break
case .TermSequence:
var operation: ConstructVariant = .Error
let count = node.children.count
if count == 0 {
print("ERROR: \(node.variant) with no children")
break
}
for i in 0...count - 1 {
let child: SyntaxTreeNode = node.children[i]
// Operation
if i % 2 == 1 {
operation = child.variant
continue
}
text += generateOutput(child)
switch operation {
case .Addition:
text += " pop \t%ecx\n"
text += " addl\t%ecx, %eax\n"
case .Subtraction:
text += " pop \t%ecx\n"
text += " subl\t%eax, %ecx\n"
text += " movl\t%ecx, %eax\n"
default:
if i != 0 {
print("Unknown operation \"\(operation)\" in \(node.variant)")
}
break
}
if i != node.children.count - 1 {
text += " push\t%eax\n"
}
}
break
case .FactorSequence:
var operation: ConstructVariant = .Error
let count = node.children.count
if count == 0 {
print("ERROR: FactorSequence with no children")
break
}
for i in 0...count - 1 {
let child: SyntaxTreeNode = node.children[i]
// Operation
if i % 2 == 1 {
operation = child.variant
continue
}
text += generateOutput(child)
switch operation {
case .Multiplication:
text += " pop \t%ecx\n"
text += " imul\t%ecx, %eax\n"
break
case .Division:
text += " movl\t%eax, %ecx\n" // Move e2 to ecx
text += " pop \t%eax\n" // Fetch e1 off stack into eax
text += " cdq\n" // Extend eax into edx
text += " idivl\t%ecx\n" // Perform edx:eax / ecx
// eax = quotient; edx rem.
break
case .Modulo:
text += " movl\t%eax, %ecx\n" // Move e2 to ecx
text += " pop \t%eax\n" // Fetch e1 off stack into eax
text += " cdq\n" // Extend eax into edx
text += " idivl\t%ecx\n" // Perform edx:eax / ecx
// eax = quotient; edx rem.
text += " movl\t%edx, %eax\n" // Move remainder to eax
break
default:
if i != 0 {
print("Unknown operation \"\(operation)\" in FactorSequence")
}
break
}
if i != node.children.count - 1 {
text += " push\t%eax\n"
}
}
break
case .Integer:
text += """
.globl \(node.value)
\(node.value):
push\t%ebp
movl\t%esp, %ebp
"""
for child in node.children {
text += generateOutput(child)
}
break
case .ReturnExpression:
if node.children.count != 1 {
print("\(node.variant) cannot have more than one child node")
return "[ERROR]"
}
text += generateOutput(node.children[0])
text += """
movl\t%ebp, %esp
pop \t%ebp
ret\n
"""
break
case .UnaryOperation:
if node.children.count != 2 {
print("\(node.variant) must have two child nodes")
}
let operation: SyntaxTreeNode = node.children[0]
let expression: SyntaxTreeNode = node.children[1]
text += generateOutput(expression)
text += generateOutput(operation)
break
case .BitwiseCompliment:
text += " not \t%eax\n"
break
case .Negation:
text += " neg \t%eax\n"
break
case .LogicalNegation:
text += " cmpl\t$0, %eax\n"
text += " movl\t$0, %eax\n"
text += " sete\t%al\n"
break
case .LiteralInteger:
text += " movl\t$\(node.value), %eax\n"
break
default:
break
}
return text
}
mutating func parse(lexed: [Substring]) -> String {
var tokens: [Token] = [Token]()
for token: Substring in lexed {
let tokenType: TokenType = categorizeToken(token: token)
tokens.append(Token(content: token, type: tokenType))
}
tokens = tokens.reversed()
let abstractSyntaxTree = SyntaxTreeNode(.Root)
if validateConstruct(type: .Program, variant: .SingleFunction, tokens: &tokens, node: abstractSyntaxTree) != .Invalid {
print("Success")
print(abstractSyntaxTree.text())
print("Assembly:")
let assembly: String = generateOutput(abstractSyntaxTree)
print(assembly)
return assembly
}
print("Distinct lack of success")
return ""
}
enum Validity {
case Valid
case Invalid
case Panic
case Break
}
func validateConstruct(type: ConstructType, variant: ConstructVariant, tokens: inout [Token], node: SyntaxTreeNode) -> Validity {
var indent: String {
var count: Int = 0
var child: SyntaxTreeNode = node
while child.parent != nil {
count += 1
child = child.parent!
}
return String(repeating: " ", count: count)
}
var loop: Bool = false
repeat {
let construct = constructDefinitions[type]![variant]!
for element in construct {
switch element {
case .OperatorLoop(let constructType):
var valid: Validity = .Invalid
var matchingOperator: Bool = false
print("\(indent)Determining need to loop construct of type \(constructType)")
let tokenBackup: [Token] = tokens
for constructVariant in constructDefinitions[constructType]!.keys {
let childNode = node.addChild(value: constructVariant)
print("\(indent)Testing operator \(constructType)::\(constructVariant)")
valid = validateConstruct(
type: constructType,
variant: constructVariant,
tokens: &tokens,
node: childNode
)
if valid == .Panic {
print("\(indent)Fail: panic (variant \"\(constructVariant)\"))\n")
return .Invalid
} else if valid == .Invalid {
tokens = tokenBackup
_ = node.popLastChild()
continue
}
matchingOperator = true
break
}
loop = matchingOperator
if loop {
print("\(indent)Looping")
} else {
print("\(indent)Not looping")
break
}
case .Optional(let tokenType):
print("\(indent)Determining need to continue. (Testing\(tokenType))")
let tokenBackup: [Token] = tokens
if let token: Token = tokens.popLast() {
if token.type == tokenType {
print("\(indent)\(tokenType) validated. Continuing")
continue
} else {
print("\(indent)\(tokenType) NOT validated. Breaking")
tokens = tokenBackup
return .Valid
}
} else {
print("No more tokens")
return .Invalid
}
case .Construct(let type):
print("\(indent)Begin validate subconstruct (type \"\(type)\")")
var valid: Validity = .Invalid
var validVariant: ConstructVariant = .Error
let tokenBackup: [Token] = tokens
var constructVariants = Array(constructDefinitions[type]!.keys)
if constructVariants.contains(.VariableExpression) {
let index: Int = constructVariants.firstIndex(of: .VariableExpression)!
let atZero: ConstructVariant = constructVariants[0]
constructVariants[0] = constructVariants[index]
constructVariants[index] = atZero
print("\(indent)Put VariableExpression at the start of the array")
}
for variant in constructVariants {
let childNode = node.addChild(value: variant)
var loopable = false
if variant == .FactorSequence || variant == .TermSequence {
loopable = true
}
print("\(indent)Testing variant \(variant) (Loop = \(loop)) (Loopable = \(loopable))")
valid = validateConstruct(
type: type,
variant: variant,
tokens: &tokens,
node: childNode
)
if valid == .Panic {
print("\(indent)Fail: panic (variant \"\(variant)\"))\n")
return .Invalid
} else if valid == .Invalid {
print("\(indent)Fail: no valid variants (variant \"\(variant)\")\n")
tokens = tokenBackup
_ = node.popLastChild()
continue
}
print("\(indent)Success (variant \"\(variant)\")\n")
validVariant = variant
break
}
if valid == .Invalid {
print("\(indent)Subconstruct validation failed")
return .Invalid
} else if valid == .Break {
print("\(indent)End validate subconstruct (variant \"\(validVariant) by breaking\")")
break
}
print("\(indent)End validate subconstruct (variant \"\(validVariant)\")")
continue
case .List(let constructType):
var valid: Validity = .Invalid
var matching: Bool = false
print("\(indent)Determining need to loop construct of type \(constructType)")
let tokenBackup: [Token] = tokens
var constructVariants = Array(constructDefinitions[constructType]!.keys)
if constructVariants.contains(.VariableExpression) {
let index: Int = constructVariants.firstIndex(of: .VariableExpression)!
let atZero: ConstructVariant = constructVariants[0]
constructVariants[0] = constructVariants[index]
constructVariants[index] = atZero
print("\(indent)Put VariableExpression at the start of the array")
}
for constructVariant in constructVariants {
let childNode = node.addChild(value: constructVariant)
print("\(indent)Testing \(constructType)::\(constructVariant)")
valid = validateConstruct(
type: constructType,
variant: constructVariant,
tokens: &tokens,
node: childNode
)
if valid == .Panic {
print("\(indent)Fail: panic (variant \"\(constructVariant)\"))\n")
return .Invalid
} else if valid == .Invalid {
tokens = tokenBackup
_ = node.popLastChild()
continue
}
matching = true
break
}
loop = matching
if loop {
print("\(indent)Looping")
} else {
print("\(indent)Not looping")
break
}
case .Token(let type):
if let token: Token = tokens.popLast() {
if token.type == .ASSIGNMENT && variant == .LogicalOrExpressionSequence {
print("\(indent)Cannot encounter assignment in \(variant)")
return .Invalid
}
else if type != token.type {
print("\(indent)Failed to validate token \"\(token.content)\". Expected \"\(type)\"")
print("\(indent)AKA, \(type) not equal to \(token.type)")
return .Invalid
}
print("\(indent)Validated token \"\(token.content)\"")
if token.type == .LITERAL_INTEGER {
node.value = String(token.content)
}
else if token.type == .IDENTIFIER {
node.value = String(token.content)
}
continue
} else {
print("\(indent)RAN OUT OF TOKENS")
return .Invalid
}
}
}
} while loop
return .Valid
}
func categorizeToken(token: Substring) -> TokenType {
if token.firstMatch(of: /^{$/) != nil { return .BRACE_OPEN }
else if token.firstMatch(of: /^}$/) != nil { return .BRACE_CLOSE }
else if token.firstMatch(of: /^\($/) != nil { return .PARENTHESIS_OPEN }
else if token.firstMatch(of: /^\)$/) != nil { return .PARENTHESIS_CLOSE }
else if token.firstMatch(of: /^;$/) != nil { return .SEMICOLON }
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: /^-$/) != nil { return .NEGATION }
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 .ADDITION }
else if token.firstMatch(of: /^\*$/) != nil { return .MULTIPLICATION }
else if token.firstMatch(of: /^\/$/) != nil { return .DIVISION }
else if token.firstMatch(of: /^&&$/) != nil { return .LOGICAL_AND }
else if token.firstMatch(of: /^\|\|$/) != nil { return .LOGICAL_OR }
else if token.firstMatch(of: /^==$/) != nil { return .EQUAL }
else if token.firstMatch(of: /^!=$/) != nil { return .NOT_EQUAL }
else if token.firstMatch(of: /^<$/) != nil { return .LESS_THAN }
else if token.firstMatch(of: /^<=$/) != nil { return .LESS_THAN_OR_EQUAL_TO }
else if token.firstMatch(of: /^>$/) != nil { return .GREATER_THAN }
else if token.firstMatch(of: /^>=$/) != nil { return .GREATER_THAN_OR_EQUAL_TO }
else if token.firstMatch(of: /^=$/) != nil { return .ASSIGNMENT }
else if token.firstMatch(of: /^%$/) != nil { return .MODULO }
else if token.firstMatch(of: /^&$/) != nil { return .BITWISE_AND }
else if token.firstMatch(of: /^\|$/) != nil { return .BITWISE_OR }
else if token.firstMatch(of: /^\^$/) != nil { return .BITWISE_XOR }
else if token.firstMatch(of: /^<<$/) != nil { return .BITWISE_SHIFT_LEFT }
else if token.firstMatch(of: /^>>$/) != nil { return .BITWISE_SHIFT_RIGHT }
else if token.firstMatch(of: /^\+=$/) != nil { return .COMPOUND_ASSIGNMENT_ADDITION }
else if token.firstMatch(of: /^-=$/) != nil { return .COMPOUND_ASSIGNMENT_SUBTRACTION }
else if token.firstMatch(of: /^\*=$/) != nil { return .COMPOUND_ASSIGNMENT_MULTIPLICATION }
else if token.firstMatch(of: /^\/=$/) != nil { return .COMPOUND_ASSIGNMENT_DIVISION }
else if token.firstMatch(of: /^%=$/) != nil { return .COMPOUND_ASSIGNMENT_MODULO }
else if token.firstMatch(of: /^<<=$/) != nil { return .COMPOUND_ASSIGNMENT_BITWISE_SHIFT_LEFT }
else if token.firstMatch(of: /^>>=$/) != nil { return .COMPOUND_ASSIGNMENT_BITWISE_SHIFT_RIGHT }
else if token.firstMatch(of: /^&=$/) != nil { return .COMPOUND_ASSIGNMENT_BITWISE_AND }
else if token.firstMatch(of: /^\|=$/) != nil { return .COMPOUND_ASSIGNMENT_BITWISE_OR }
else if token.firstMatch(of: /^\^=$/) != nil { return .COMPOUND_ASSIGNMENT_BITWISE_XOR }
return .UNDEFINED
}
}