1682 lines
57 KiB
Swift
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
|
|
}
|
|
}
|