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
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<String, Construct>
}
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
}

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