From 9df9392972f347dab00eacd1ba48e6de15d71447 Mon Sep 17 00:00:00 2001 From: Trevor Maze Date: Mon, 26 Jan 2026 01:35:51 -0500 Subject: [PATCH] End of Stage 1 --- Sources/rxcc/rxcc.swift | 225 ++++++++++++++++++++++++++++++---------- c/a.out | Bin 15712 -> 0 bytes c/return_2 | Bin 14976 -> 0 bytes c/return_2.c | 4 - c/return_2.s | 35 ------- 5 files changed, 168 insertions(+), 96 deletions(-) delete mode 100755 c/a.out delete mode 100755 c/return_2 delete mode 100644 c/return_2.c delete mode 100644 c/return_2.s diff --git a/Sources/rxcc/rxcc.swift b/Sources/rxcc/rxcc.swift index b90e410..43bafa7 100644 --- a/Sources/rxcc/rxcc.swift +++ b/Sources/rxcc/rxcc.swift @@ -1,22 +1,5 @@ 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 { case BRACE_OPEN case BRACE_CLOSE @@ -27,10 +10,15 @@ enum TokenType { case RETURN case IDENTIFIER case LITERAL_INTEGER + case NEGATION + case BITWISE_COMPLIMENT + case LOGICAL_NEGATION case UNDEFINED } +let UNARY_OPERATORS: [TokenType] = [.NEGATION, .BITWISE_COMPLIMENT, .LOGICAL_NEGATION] + struct Token { let content: Substring let type: TokenType @@ -39,36 +27,74 @@ struct Token { typealias Construct = [Element] enum Element { - case Construct(type: Construct) - case Token(type: TokenType) + case Construct(type: ConstructDefinitions); + case Token(types: [TokenType]) } +enum ConstructType { + case Expression + case Statement + case Function + case Program +} +struct ConstructDefinitions { + var type: ConstructType + var variants: Dictionary +} -let expression: Construct = [ - .Token(type: .LITERAL_INTEGER) -] -let statement: Construct = [ - .Token(type: .RETURN), - .Construct(type: expression), - .Token(type: .SEMICOLON) -] -let function: Construct = [ - .Token(type: .INT), - .Token(type: .IDENTIFIER), - .Token(type: .PARENTHESIS_OPEN), - .Token(type: .PARENTHESIS_CLOSE), - .Token(type: .BRACE_OPEN), - .Construct(type: statement), - .Token(type: .BRACE_CLOSE) -] -let program: Construct = [ - .Construct(type: function) -] +let expression: ConstructDefinitions = ConstructDefinitions( + type: .Expression, + variants: [ + "LiteralInteger": [ + .Token(types: [.LITERAL_INTEGER]) + ], + "UnaryOperator": [ + .Token(types: [.NEGATION]), + .Token(types: UNARY_OPERATORS) + ], + ] +) + +let statement: ConstructDefinitions = ConstructDefinitions( + type: .Statement, + variants: [ + "ReturnInteger": [ + .Token(types: [.RETURN]), + .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 { var name: String + var path: String var contents: String var valid: Bool } @@ -76,12 +102,50 @@ struct TestFile { @main struct rxcc { static func main() { - for testFile in getTestFiles() { - print("Testing \(testFile.valid ? "valid" : "invalid") file \(testFile.name):") - print(testFile.contents) - let lexed: [Substring] = lex(string: testFile.contents) - parse(lexed: lexed) - print() + if CommandLine.arguments.count < 2 { + for testFile in getTestFiles() { + print("Testing \(testFile.valid ? "valid" : "invalid") file \(testFile.name):") + print(testFile.contents) + 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\"") + } } } } @@ -90,23 +154,28 @@ func getTestFiles() -> [TestFile] { var testFiles: [TestFile] = [TestFile]() let fileManager = FileManager.default - let path = "c/tests/stage_1" + let path = "c/tests/stage_2" 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, contents: text, valid: true)) + 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, contents: text, valid: false)) + 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.") @@ -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: " ! ") do { let tokens: [Substring] = line.split(separator: try Regex(" +")) + print("Tokens:") + print(tokens) return tokens } catch { print("Regex did something wrong") @@ -133,7 +207,7 @@ func lex(string: String) -> [Substring] { return [] } -func parse(lexed: [Substring]) { +func parse(lexed: [Substring]) -> String { var tokens: [Token] = [Token]() for token: Substring in lexed { let tokenType: TokenType = categorizeToken(token: token) @@ -141,32 +215,66 @@ func parse(lexed: [Substring]) { } tokens = tokens.reversed() - if validateConstruct(program, tokens: &tokens) { + var output: String = "" + + if validateConstruct(program.variants["Function"]!, constructType: .Function, tokens: &tokens, output: &output) { print("Success") - return + print("Assembly:") + print(output) + print() + + return output } 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 { switch element { case .Construct(let type): - print("Begin validate subconstruct") - if !validateConstruct(type, tokens: &tokens) { + print("Begin validate subconstruct (type \"\(type.type)\")") + + 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") return false } - print("End validate subconstruct") + print("End validate subconstruct (variant \"\(validVariant)\")") break case .Token(let type): if let token: Token = tokens.popLast() { - if type != token.type { + if !type.contains(token.type) { print("VALIDATION FAILED FOR TOKEN \"\(token.content)\"") 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 { print("RAN OUT OF TOKENS") 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: /^[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 } return .UNDEFINED } diff --git a/c/a.out b/c/a.out deleted file mode 100755 index 90f76cf33de22d9b15d378257d4cbb298adb71e3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15712 zcmeHOZ)_Y#6`wmh$)!!=i(3abiO3d3RVv|)?IczUxIM={XN{c1jU6Ne?XtdG+b7*0 zbGL`=R;U&MRI5~^2r49mREaPBT3U$~RLNFQMa7pCfmHQVKvV<-)Kvim5I)}9dB62~ za|XnRN{AWj_BZc0@4cCOJG(dQee+~~ay*?%DU=TNkYdToY9=QQpBs`2K~6oO4$$$a zI-+)y+!=DZJ*R&*>0zj;Q>(rD)}C4w~HG@PU3)c z07QG#pT)*htne7&H$?=-?RY3zTPtKA+Kwu%<%}TgSY$g(>@2YZJ}=`0BAy%*{!B?c zlbEA6F1heX@ezdGRk2IranS>^J{Q;pRw$|@3LS+DcGK?!MXo2u2RWzR-8RlCvFjAa zNyIz>qQ6JTPNsx-{?~x%aY_2y>HKZTc>szS#-*RS;JJ40!ey0SQENOFw5L>Ro19L_ zta~?o_;j%SXY=cCfBU89KEK<3;l$?n4?pq#H{1tLmP@lkr%#rPedSWMxzxAxz);`N zX}eLg2g822FZ4&%|EREjcN@)7;;c5jy8~a{fxnCRkF@clT$jF0!EFRyJ#Z^tsY*;? z$#dsQRj*un+*jPp-)%^po18d1>JHk2_L-=D>a=nvX3o1szwXbM8i8M*IX_yiRs9)n zw(OIy`AV%S-d(NQR#DLfN#}_N@<%JmC=O&n%&x*a^^wTCs&b2fese-h)!v z2NMAk0TTfe0TTfe0TTfef&Z5Xyx;ZVzd2WapK+evwSH15=PPSLx^>;T^5e|4Fa+E_ z@P~=jHy?9Wf8|{H^~Tgpeqe3j4d~%&CTF$a?mFZGOZhP zUB||@?0I}E!+B~}%~O}1E5mni)meQf*zY_&d>;v|o?G-ReY4PNwO-$Kl!(-$*S5w> z~~mXdH(*L>b+J{i{qK1r+wsyTcW`E!0q&p z5ua9fswpJiivGRAuOxW>Ujyr7zKXi~Bvn%CLn@oo49?G|LcUvJ{^?ewzDO1JgZU4B zHPk<-FrUG{K$Ujme`yE)wa`zOTC}K=DDC|Glyp++E|olQKM!pWs7`CUli!5;J?f_9 z_oDS5RB4yjeV77bK`Dm$kUykUE=E#yi1@5J6vY#*!^C%|_bFWe;73J2>1QhR53Mb9 zi9eV=q%aTBQf6M!UB=Q3{zvo#@mV^*y*X%VoFKlN&Z8ysB>dkLHLZv~<^!z0Ci?ft z{R{VxXNl+WB=x^byanSJ{(yMgBV)As8S&j|x=SVIt(q>>g8{on8S36_$ql@DMZ_%S zuve#v-AZS7gNj?=g!Trh7HjTξ&S-C|IyH(al|qzbjlV%ZP;qTSzKgH!2Cu2-*n z%dTGy>dR`b?p1uZ*sN5R$s{JZq#0~et@}Z59FH}6QRGj>V2=O!S+UbV=Z046SyRlxo#&f=g|6zwEIA`zZy=4*VrL@(iH1 zS{OasUyyUYP$UaTGMJIG6MZ}zLwh|Hv_0zN+^dud)N_qI)4^UWQ$@R`pembYWQ8VL z{4)5j>%4y8-|Vz_z&;^hPIv%*&mr?Rs<{1C`5^`#hoT_vFQ7-LLYHvV|4#eVpwGJT zGC{&$XbphzUzh%YSpUd!A=;M(AJbIfdlKw7#2$E66tQ0f-yiFj&rmy?z3P1qOej9;e@#{}z6{H_2y69I#Jw&n6I`o!(=I|;=4IaH=Ya{hfs z?4#dsOEd+O`jCO&6MMuD>jog^pLo5k{}&VXmh=mpfuSJc5Bf(`iC=$M2Ldf93S!)# z-=N9?`pC48)?wI}m+TKR@E2s;6|={>ua|EORG_D&EgAeTse#Wu`p0?<`!k>ClS>8j z19q4PzoRx*fj#zjt({~mrA}ywj<5&5OD%DGtnY4$J+5;&fF1Bw!d~S>vtJ{|DeP_W z*rW#bh4EuOlofmQ1A7SvMAbnAbD|#cuuoopu#Y3=Ze+~<#GxYtZ$EWL8|+IMBt-R3G3{!_ diff --git a/c/return_2 b/c/return_2 deleted file mode 100755 index 096aa212558d90343a75202238b1ae567f58281d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14976 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 Jjj+k&KLDuaAPN8g diff --git a/c/return_2.c b/c/return_2.c deleted file mode 100644 index 76598f0..0000000 --- a/c/return_2.c +++ /dev/null @@ -1,4 +0,0 @@ -int main() { - return 2; -} - diff --git a/c/return_2.s b/c/return_2.s deleted file mode 100644 index b949824..0000000 --- a/c/return_2.s +++ /dev/null @@ -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