End of Stage 1

This commit is contained in:
2026-01-26 01:35:51 -05:00
parent 9fd722edd9
commit 9df9392972
5 changed files with 168 additions and 96 deletions

View File

@@ -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/a.out

Binary file not shown.

Binary file not shown.

View File

@@ -1,4 +0,0 @@
int main() {
return 2;
}

View File

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