End of Stage 1
This commit is contained in:
@@ -1,22 +1,5 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
enum Constant {
|
|
||||||
case Integer(value: Int)
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Return {
|
|
||||||
let value: Constant
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Function {
|
|
||||||
let name: String
|
|
||||||
let statement: Return
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Program {
|
|
||||||
let contents: Function
|
|
||||||
}
|
|
||||||
|
|
||||||
enum TokenType {
|
enum TokenType {
|
||||||
case BRACE_OPEN
|
case BRACE_OPEN
|
||||||
case BRACE_CLOSE
|
case BRACE_CLOSE
|
||||||
@@ -27,10 +10,15 @@ enum TokenType {
|
|||||||
case RETURN
|
case RETURN
|
||||||
case IDENTIFIER
|
case IDENTIFIER
|
||||||
case LITERAL_INTEGER
|
case LITERAL_INTEGER
|
||||||
|
case NEGATION
|
||||||
|
case BITWISE_COMPLIMENT
|
||||||
|
case LOGICAL_NEGATION
|
||||||
|
|
||||||
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
|
||||||
@@ -39,36 +27,74 @@ struct Token {
|
|||||||
typealias Construct = [Element]
|
typealias Construct = [Element]
|
||||||
|
|
||||||
enum Element {
|
enum Element {
|
||||||
case Construct(type: Construct)
|
case Construct(type: ConstructDefinitions);
|
||||||
case Token(type: TokenType)
|
case Token(types: [TokenType])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum ConstructType {
|
||||||
|
case Expression
|
||||||
|
case Statement
|
||||||
|
case Function
|
||||||
|
case Program
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ConstructDefinitions {
|
||||||
|
var type: ConstructType
|
||||||
|
var variants: Dictionary<String, Construct>
|
||||||
|
}
|
||||||
|
|
||||||
let expression: Construct = [
|
let expression: ConstructDefinitions = ConstructDefinitions(
|
||||||
.Token(type: .LITERAL_INTEGER)
|
type: .Expression,
|
||||||
]
|
variants: [
|
||||||
let statement: Construct = [
|
"LiteralInteger": [
|
||||||
.Token(type: .RETURN),
|
.Token(types: [.LITERAL_INTEGER])
|
||||||
.Construct(type: expression),
|
],
|
||||||
.Token(type: .SEMICOLON)
|
"UnaryOperator": [
|
||||||
]
|
.Token(types: [.NEGATION]),
|
||||||
let function: Construct = [
|
.Token(types: UNARY_OPERATORS)
|
||||||
.Token(type: .INT),
|
],
|
||||||
.Token(type: .IDENTIFIER),
|
]
|
||||||
.Token(type: .PARENTHESIS_OPEN),
|
)
|
||||||
.Token(type: .PARENTHESIS_CLOSE),
|
|
||||||
.Token(type: .BRACE_OPEN),
|
let statement: ConstructDefinitions = ConstructDefinitions(
|
||||||
.Construct(type: statement),
|
type: .Statement,
|
||||||
.Token(type: .BRACE_CLOSE)
|
variants: [
|
||||||
]
|
"ReturnInteger": [
|
||||||
let program: Construct = [
|
.Token(types: [.RETURN]),
|
||||||
.Construct(type: function)
|
.Construct(type: expression),
|
||||||
]
|
.Token(types: [.SEMICOLON])
|
||||||
|
]
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
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]),
|
||||||
|
.Construct(type: statement),
|
||||||
|
.Token(types: [.BRACE_CLOSE])
|
||||||
|
]
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
let program: ConstructDefinitions = ConstructDefinitions(
|
||||||
|
type: .Function,
|
||||||
|
variants: [
|
||||||
|
"Function": [
|
||||||
|
.Construct(type: function)
|
||||||
|
]
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
struct TestFile {
|
struct TestFile {
|
||||||
var name: String
|
var name: String
|
||||||
|
var path: String
|
||||||
var contents: String
|
var contents: String
|
||||||
var valid: Bool
|
var valid: Bool
|
||||||
}
|
}
|
||||||
@@ -76,12 +102,50 @@ struct TestFile {
|
|||||||
@main
|
@main
|
||||||
struct rxcc {
|
struct rxcc {
|
||||||
static func main() {
|
static func main() {
|
||||||
for testFile in getTestFiles() {
|
if CommandLine.arguments.count < 2 {
|
||||||
print("Testing \(testFile.valid ? "valid" : "invalid") file \(testFile.name):")
|
for testFile in getTestFiles() {
|
||||||
print(testFile.contents)
|
print("Testing \(testFile.valid ? "valid" : "invalid") file \(testFile.name):")
|
||||||
let lexed: [Substring] = lex(string: testFile.contents)
|
print(testFile.contents)
|
||||||
parse(lexed: lexed)
|
let lexed: [Substring] = lex(string: testFile.contents)
|
||||||
print()
|
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\"")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -90,23 +154,28 @@ func getTestFiles() -> [TestFile] {
|
|||||||
var testFiles: [TestFile] = [TestFile]()
|
var testFiles: [TestFile] = [TestFile]()
|
||||||
|
|
||||||
let fileManager = FileManager.default
|
let fileManager = FileManager.default
|
||||||
let path = "c/tests/stage_1"
|
let path = "c/tests/stage_2"
|
||||||
|
|
||||||
do {
|
do {
|
||||||
let validItems = try fileManager.contentsOfDirectory(atPath: path + "/valid")
|
let validItems = try fileManager.contentsOfDirectory(atPath: path + "/valid")
|
||||||
|
|
||||||
for item in validItems {
|
for item in validItems {
|
||||||
|
if item.last?.lowercased() != "c" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
let fileURL = URL(fileURLWithPath: path + "/valid").appendingPathComponent(item)
|
let fileURL = URL(fileURLWithPath: path + "/valid").appendingPathComponent(item)
|
||||||
let text = try String(contentsOf: fileURL, encoding: .utf8)
|
let text = try String(contentsOf: fileURL, encoding: .utf8)
|
||||||
testFiles.append(TestFile(name: item, contents: text, valid: true))
|
testFiles.append(TestFile(name: item, path: path + "/valid/", contents: text, valid: true))
|
||||||
}
|
}
|
||||||
|
|
||||||
let invalidItems = try fileManager.contentsOfDirectory(atPath: path + "/invalid")
|
let invalidItems = try fileManager.contentsOfDirectory(atPath: path + "/invalid")
|
||||||
|
|
||||||
for item in invalidItems {
|
for item in invalidItems {
|
||||||
|
if item.last?.lowercased() != "c" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
let fileURL = URL(fileURLWithPath: path + "/invalid").appendingPathComponent(item)
|
let fileURL = URL(fileURLWithPath: path + "/invalid").appendingPathComponent(item)
|
||||||
let text = try String(contentsOf: fileURL, encoding: .utf8)
|
let text = try String(contentsOf: fileURL, encoding: .utf8)
|
||||||
testFiles.append(TestFile(name: item, contents: text, valid: false))
|
testFiles.append(TestFile(name: item, path: path + "/invalid/", contents: text, valid: false))
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
print("You think I'm going to handle errors? You are mistaken.")
|
print("You think I'm going to handle errors? You are mistaken.")
|
||||||
@@ -123,9 +192,14 @@ func lex(string: String) -> [Substring] {
|
|||||||
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 {
|
do {
|
||||||
let tokens: [Substring] = line.split(separator: try Regex(" +"))
|
let tokens: [Substring] = line.split(separator: try Regex(" +"))
|
||||||
|
print("Tokens:")
|
||||||
|
print(tokens)
|
||||||
return tokens
|
return tokens
|
||||||
} catch {
|
} catch {
|
||||||
print("Regex did something wrong")
|
print("Regex did something wrong")
|
||||||
@@ -133,7 +207,7 @@ func lex(string: String) -> [Substring] {
|
|||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
func parse(lexed: [Substring]) {
|
func parse(lexed: [Substring]) -> String {
|
||||||
var tokens: [Token] = [Token]()
|
var tokens: [Token] = [Token]()
|
||||||
for token: Substring in lexed {
|
for token: Substring in lexed {
|
||||||
let tokenType: TokenType = categorizeToken(token: token)
|
let tokenType: TokenType = categorizeToken(token: token)
|
||||||
@@ -141,32 +215,66 @@ func parse(lexed: [Substring]) {
|
|||||||
}
|
}
|
||||||
tokens = tokens.reversed()
|
tokens = tokens.reversed()
|
||||||
|
|
||||||
if validateConstruct(program, tokens: &tokens) {
|
var output: String = ""
|
||||||
|
|
||||||
|
if validateConstruct(program.variants["Function"]!, constructType: .Function, tokens: &tokens, output: &output) {
|
||||||
print("Success")
|
print("Success")
|
||||||
return
|
print("Assembly:")
|
||||||
|
print(output)
|
||||||
|
print()
|
||||||
|
|
||||||
|
return output
|
||||||
}
|
}
|
||||||
|
|
||||||
print("Distinct lack of success")
|
print("Distinct lack of success")
|
||||||
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateConstruct(_ construct: Construct, tokens: inout [Token]) -> Bool {
|
func validateConstruct(_ construct: Construct, constructType: ConstructType, tokens: inout [Token], output: inout String) -> 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")
|
print("Begin validate subconstruct (type \"\(type.type)\")")
|
||||||
if !validateConstruct(type, tokens: &tokens) {
|
|
||||||
|
var valid: Bool = false
|
||||||
|
var validVariant: String = ""
|
||||||
|
for key in type.variants.keys {
|
||||||
|
if !validateConstruct(type.variants[key]!, constructType: type.type, tokens: &tokens, output: &output) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
valid = true
|
||||||
|
validVariant = key
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if !valid {
|
||||||
print("Subconstruct validation failed")
|
print("Subconstruct validation failed")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
print("End validate subconstruct")
|
print("End validate subconstruct (variant \"\(validVariant)\")")
|
||||||
break
|
break
|
||||||
|
|
||||||
case .Token(let type):
|
case .Token(let type):
|
||||||
if let token: Token = tokens.popLast() {
|
if let token: Token = tokens.popLast() {
|
||||||
if type != token.type {
|
if !type.contains(token.type) {
|
||||||
print("VALIDATION FAILED FOR TOKEN \"\(token.content)\"")
|
print("VALIDATION FAILED FOR TOKEN \"\(token.content)\"")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch constructType {
|
||||||
|
case .Function:
|
||||||
|
if token.type == .IDENTIFIER {
|
||||||
|
output.append(" .globl \(token.content)\n\(token.content):\n")
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case .Expression:
|
||||||
|
if token.type == .LITERAL_INTEGER {
|
||||||
|
output.append(" movl $\(token.content), %eax\n ret\n")
|
||||||
|
}
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
print("RAN OUT OF TOKENS")
|
print("RAN OUT OF TOKENS")
|
||||||
return false
|
return false
|
||||||
@@ -187,6 +295,9 @@ func categorizeToken(token: Substring) -> TokenType {
|
|||||||
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 .BITWISE_COMPLIMENT }
|
||||||
|
else if token.firstMatch(of: /^!$/) != nil { return .LOGICAL_NEGATION }
|
||||||
|
|
||||||
return .UNDEFINED
|
return .UNDEFINED
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
c/return_2
BIN
c/return_2
Binary file not shown.
@@ -1,4 +0,0 @@
|
|||||||
int main() {
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
35
c/return_2.s
35
c/return_2.s
@@ -1,35 +0,0 @@
|
|||||||
.file "return_2.c"
|
|
||||||
.text
|
|
||||||
.globl main
|
|
||||||
.type main, @function
|
|
||||||
main:
|
|
||||||
.LFB0:
|
|
||||||
.cfi_startproc
|
|
||||||
pushl %ebp
|
|
||||||
.cfi_def_cfa_offset 8
|
|
||||||
.cfi_offset 5, -8
|
|
||||||
movl %esp, %ebp
|
|
||||||
.cfi_def_cfa_register 5
|
|
||||||
call __x86.get_pc_thunk.ax
|
|
||||||
addl $_GLOBAL_OFFSET_TABLE_, %eax
|
|
||||||
movl $2, %eax
|
|
||||||
popl %ebp
|
|
||||||
.cfi_restore 5
|
|
||||||
.cfi_def_cfa 4, 4
|
|
||||||
ret
|
|
||||||
.cfi_endproc
|
|
||||||
.LFE0:
|
|
||||||
.size main, .-main
|
|
||||||
.section .text.__x86.get_pc_thunk.ax,"axG",@progbits,__x86.get_pc_thunk.ax,comdat
|
|
||||||
.globl __x86.get_pc_thunk.ax
|
|
||||||
.hidden __x86.get_pc_thunk.ax
|
|
||||||
.type __x86.get_pc_thunk.ax, @function
|
|
||||||
__x86.get_pc_thunk.ax:
|
|
||||||
.LFB1:
|
|
||||||
.cfi_startproc
|
|
||||||
movl (%esp), %eax
|
|
||||||
ret
|
|
||||||
.cfi_endproc
|
|
||||||
.LFE1:
|
|
||||||
.ident "GCC: (GNU) 15.2.1 20260103"
|
|
||||||
.section .note.GNU-stack,"",@progbits
|
|
||||||
Reference in New Issue
Block a user