From 4ff6fcf9fee3efe7a56825846c2e7b4ca9f3742b Mon Sep 17 00:00:00 2001 From: kntkymt Date: Sat, 16 Dec 2023 00:22:24 +0900 Subject: [PATCH 1/3] Support VariableDecl on toplevel --- Sources/Generator/Generator.swift | 2 +- Sources/Parser/Node/StatementNode.swift | 54 +++++++---- Sources/Parser/Parser.swift | 59 ++++++------ Tests/ParserTest/FunctionTest.swift | 122 ++++++++++++++---------- Tests/ParserTest/VariableTest.swift | 39 ++++++++ 5 files changed, 179 insertions(+), 97 deletions(-) diff --git a/Sources/Generator/Generator.swift b/Sources/Generator/Generator.swift index a364b55..08a2934 100644 --- a/Sources/Generator/Generator.swift +++ b/Sources/Generator/Generator.swift @@ -123,7 +123,7 @@ public final class Generator { var parameterDecl = "" // 引数をローカル変数として保存し直す - for (index, parameter) in casted.parameters.enumerated() { + for (index, parameter) in casted.parameterNodes.enumerated() { let offset = (variables.count + 1) * 8 variables[parameter.identifierName] = VariableInfo(type: parameter.type, addressOffset: offset) diff --git a/Sources/Parser/Node/StatementNode.swift b/Sources/Parser/Node/StatementNode.swift index ab2ff54..ab9aab9 100644 --- a/Sources/Parser/Node/StatementNode.swift +++ b/Sources/Parser/Node/StatementNode.swift @@ -131,26 +131,42 @@ public class FunctionDeclNode: NodeProtocol { // MARK: - Property public var kind: NodeKind = .functionDecl - public let sourceTokens: [Token] - public var children: [any NodeProtocol] { [block] + parameters } + public var sourceTokens: [Token] { + returnTypeNode.sourceTokens + + [functionNameToken, parenthesisLeftToken] + + parameterNodes.flatMap { $0.sourceTokens } + + [parenthesisRightToken] + + block.sourceTokens + } + public var children: [any NodeProtocol] { [returnTypeNode] + parameterNodes + [block] } - public let returnType: any NodeProtocol - public let token: Token + public let returnTypeNode: any NodeProtocol + public let functionNameToken: Token + public let parenthesisLeftToken: Token + public let parameterNodes: [VariableDeclNode] + public let parenthesisRightToken: Token public let block: BlockStatementNode - public let parameters: [VariableDeclNode] public var functionName: String { - token.value + functionNameToken.value } // MARK: - Initializer - init(returnType: any NodeProtocol, token: Token, block: BlockStatementNode, parameters: [VariableDeclNode], sourceTokens: [Token]) { - self.returnType = returnType - self.token = token + init( + returnTypeNode: any NodeProtocol, + functionNameToken: Token, + parenthesisLeftToken: Token, + parameterNodes: [VariableDeclNode], + parenthesisRightToken: Token, + block: BlockStatementNode + ) { + self.returnTypeNode = returnTypeNode + self.functionNameToken = functionNameToken + self.parenthesisLeftToken = parenthesisLeftToken + self.parameterNodes = parameterNodes + self.parenthesisRightToken = parenthesisRightToken self.block = block - self.parameters = parameters - self.sourceTokens = sourceTokens } } @@ -162,7 +178,7 @@ public class VariableDeclNode: NodeProtocol { public var sourceTokens: [Token] { type.sourceTokens + [identifierToken] } - public let children: [any NodeProtocol] = [] + public var children: [any NodeProtocol] { [type] } public let type: any TypeNodeProtocol public let identifierToken: Token @@ -184,15 +200,21 @@ public class SourceFileNode: NodeProtocol { // MARK: - Property public var kind: NodeKind = .sourceFile - public let sourceTokens: [Token] - public var children: [any NodeProtocol] { functions } + public var sourceTokens: [Token] { + functions.flatMap { $0.sourceTokens } + globalVariables.flatMap { $0.sourceTokens } + } + + public var children: [any NodeProtocol] { + functions + globalVariables + } public let functions: [FunctionDeclNode] + public let globalVariables: [VariableDeclNode] // MARK: - Initializer - init(functions: [FunctionDeclNode], sourceTokens: [Token]) { + init(functions: [FunctionDeclNode], globalVariables: [VariableDeclNode]) { self.functions = functions - self.sourceTokens = sourceTokens + self.globalVariables = globalVariables } } diff --git a/Sources/Parser/Parser.swift b/Sources/Parser/Parser.swift index 44feb5e..39e0a5d 100644 --- a/Sources/Parser/Parser.swift +++ b/Sources/Parser/Parser.swift @@ -105,43 +105,44 @@ public final class Parser { // MARK: - Syntax - // program = functionDecl* + // program = (functionDecl | variableDecl ";")* func program() throws -> SourceFileNode { var functionDecls: [FunctionDeclNode] = [] + var globalVariableDecls: [VariableDeclNode] = [] while index < tokens.count { - functionDecls.append(try functionDecl()) - } - return SourceFileNode(functions: functionDecls, sourceTokens: tokens) - } + let type = try type() + let identifier = try consumeIdentifierToken() - // functionDecl = type ident "(" functionParameters? ")" block - func functionDecl() throws -> FunctionDeclNode { - if index >= tokens.count { - throw ParseError.invalidSyntax(index: tokens.last.map { $0.sourceIndex + 1 } ?? 0) - } - let startIndex = index + // functionDecl = type ident "(" functionParameters? ")" block + if index < tokens.count, case .reserved(.parenthesisLeft, _) = tokens[index] { + let parenthesisLeft = try consumeReservedToken(.parenthesisLeft) - let returnType = try type() + var parameters: [VariableDeclNode] = [] + if index < tokens.count, case .type = tokens[index] { + parameters = try functionParameters() + } - let functionName = try consumeIdentifierToken() - try consumeReservedToken(.parenthesisLeft) + let parenthesisRight = try consumeReservedToken(.parenthesisRight) - var parameters: [VariableDeclNode] = [] - if index < tokens.count, case .type = tokens[index] { - parameters = try functionParameters() + let function = FunctionDeclNode( + returnTypeNode: type, + functionNameToken: identifier, + parenthesisLeftToken: parenthesisLeft, + parameterNodes: parameters, + parenthesisRightToken: parenthesisRight, + block: try block() + ) + functionDecls.append(function) + } else { + let variable = try variableDecl(variableType: type, identifier: identifier) + try consumeReservedToken(.semicolon) + globalVariableDecls.append(variable) + } } - try consumeReservedToken(.parenthesisRight) - - return FunctionDeclNode( - returnType: returnType, - token: functionName, - block: try block(), - parameters: parameters, - sourceTokens: Array(tokens[startIndex.. VariableDeclNode { - var type = try type() - let identifier = try consumeIdentifierToken() + func variableDecl(variableType: (any TypeNodeProtocol)? = nil, identifier: Token? = nil) throws -> VariableDeclNode { + var type = if let variableType { variableType } else { try type() } + let identifier = if let identifier { identifier } else { try consumeIdentifierToken() } if index < tokens.count, case .reserved(.squareLeft, _) = tokens[index] { type = ArrayTypeNode( diff --git a/Tests/ParserTest/FunctionTest.swift b/Tests/ParserTest/FunctionTest.swift index fc81cef..518a2fd 100644 --- a/Tests/ParserTest/FunctionTest.swift +++ b/Tests/ParserTest/FunctionTest.swift @@ -17,22 +17,28 @@ final class FunctionTest: XCTestCase { .reserved(.semicolon, sourceIndex: 14), .reserved(.braceRight, sourceIndex: 15) ] - let node = try Parser(tokens: tokens).functionDecl() + let node = try Parser(tokens: tokens).parse() XCTAssertEqual( node, - FunctionDeclNode( - returnType: TypeNode(typeToken: tokens[0]), - token: tokens[1], - block: BlockStatementNode( - statements: [ - IntegerLiteralNode(token: tokens[5]), - IntegerLiteralNode(token: tokens[7]) - ], - sourceTokens: Array(tokens[4...9]) - ), - parameters: [], - sourceTokens: tokens + SourceFileNode( + functions: [ + FunctionDeclNode( + returnTypeNode: TypeNode(typeToken: tokens[0]), + functionNameToken: tokens[1], + parenthesisLeftToken: tokens[2], + parameterNodes: [], + parenthesisRightToken: tokens[3], + block: BlockStatementNode( + statements: [ + IntegerLiteralNode(token: tokens[5]), + IntegerLiteralNode(token: tokens[7]) + ], + sourceTokens: Array(tokens[4...9]) + ) + ) + ], + globalVariables: [] ) ) } @@ -51,22 +57,28 @@ final class FunctionTest: XCTestCase { .reserved(.semicolon, sourceIndex: 15), .reserved(.braceRight, sourceIndex: 16) ] - let node = try Parser(tokens: tokens).functionDecl() + let node = try Parser(tokens: tokens).parse() XCTAssertEqual( node, - FunctionDeclNode( - returnType: PointerTypeNode(referenceType: TypeNode(typeToken: tokens[0]), pointerToken: tokens[1]), - token: tokens[2], - block: BlockStatementNode( - statements: [ - IntegerLiteralNode(token: tokens[6]), - IntegerLiteralNode(token: tokens[8]) - ], - sourceTokens: Array(tokens[5...10]) - ), - parameters: [], - sourceTokens: tokens + SourceFileNode( + functions: [ + FunctionDeclNode( + returnTypeNode: PointerTypeNode(referenceType: TypeNode(typeToken: tokens[0]), pointerToken: tokens[1]), + functionNameToken: tokens[2], + parenthesisLeftToken: tokens[3], + parameterNodes: [], + parenthesisRightToken: tokens[4], + block: BlockStatementNode( + statements: [ + IntegerLiteralNode(token: tokens[6]), + IntegerLiteralNode(token: tokens[8]) + ], + sourceTokens: Array(tokens[5...10]) + ) + ) + ], + globalVariables: [] ) ) } @@ -102,24 +114,30 @@ final class FunctionTest: XCTestCase { .reserved(.semicolon, sourceIndex: 23), .reserved(.braceRight, sourceIndex: 24) ] - let node = try Parser(tokens: tokens).functionDecl() + let node = try Parser(tokens: tokens).parse() XCTAssertEqual( node, - FunctionDeclNode( - returnType: TypeNode(typeToken: tokens[0]), - token: tokens[1], - block: BlockStatementNode( - statements: [ - IntegerLiteralNode(token: tokens[10]) - ], - sourceTokens: Array(tokens[9...12]) - ), - parameters: [ - VariableDeclNode(type: TypeNode(typeToken: tokens[3]), identifierToken: tokens[4]), - VariableDeclNode(type: TypeNode(typeToken: tokens[6]), identifierToken: tokens[7]) + SourceFileNode( + functions: [ + FunctionDeclNode( + returnTypeNode: TypeNode(typeToken: tokens[0]), + functionNameToken: tokens[1], + parenthesisLeftToken: tokens[2], + parameterNodes: [ + VariableDeclNode(type: TypeNode(typeToken: tokens[3]), identifierToken: tokens[4]), + VariableDeclNode(type: TypeNode(typeToken: tokens[6]), identifierToken: tokens[7]) + ], + parenthesisRightToken: tokens[8], + block: BlockStatementNode( + statements: [ + IntegerLiteralNode(token: tokens[10]) + ], + sourceTokens: Array(tokens[9...12]) + ) + ) ], - sourceTokens: tokens + globalVariables: [] ) ) } @@ -175,31 +193,33 @@ final class FunctionTest: XCTestCase { SourceFileNode( functions: [ FunctionDeclNode( - returnType: TypeNode(typeToken: tokens[0]), - token: tokens[1], + returnTypeNode: TypeNode(typeToken: tokens[0]), + functionNameToken: tokens[1], + parenthesisLeftToken: tokens[2], + parameterNodes: [], + parenthesisRightToken: tokens[3], block: BlockStatementNode( statements: [ IntegerLiteralNode(token: tokens[5]) ], sourceTokens: Array(tokens[4...7]) - ), - parameters: [], - sourceTokens: Array(tokens[0...7]) + ) ), FunctionDeclNode( - returnType: TypeNode(typeToken: tokens[8]), - token: tokens[9], + returnTypeNode: TypeNode(typeToken: tokens[8]), + functionNameToken: tokens[9], + parenthesisLeftToken: tokens[10], + parameterNodes: [], + parenthesisRightToken: tokens[11], block: BlockStatementNode( statements: [ IntegerLiteralNode(token: tokens[13]) ], sourceTokens: Array(tokens[12...15]) - ), - parameters: [], - sourceTokens: Array(tokens[8...15]) + ) ) ], - sourceTokens: tokens + globalVariables: [] ) ) } diff --git a/Tests/ParserTest/VariableTest.swift b/Tests/ParserTest/VariableTest.swift index bd1e97d..33b925e 100644 --- a/Tests/ParserTest/VariableTest.swift +++ b/Tests/ParserTest/VariableTest.swift @@ -99,4 +99,43 @@ final class VariableTest: XCTestCase { ) ) } + + func testGlobalVariableDecl() throws { + let tokens: [Token] = [ + .type(.int, sourceIndex: 0), + .identifier("a", sourceIndex: 4), + .reserved(.semicolon, sourceIndex: 5) + ] + let node = try Parser(tokens: tokens).parse() + + XCTAssertEqual( + node, + SourceFileNode( + functions: [], + globalVariables: [ + VariableDeclNode(type: TypeNode(typeToken: tokens[0]), identifierToken: tokens[1]) + ] + ) + ) + } + + func testGlobalVariableDeclPointer() throws { + let tokens: [Token] = [ + .type(.int, sourceIndex: 0), + .reserved(.mul, sourceIndex: 3), + .identifier("a", sourceIndex: 5), + .reserved(.semicolon, sourceIndex: 6) + ] + let node = try Parser(tokens: tokens).parse() + + XCTAssertEqual( + node, + SourceFileNode( + functions: [], + globalVariables: [ + VariableDeclNode(type: PointerTypeNode(referenceType: TypeNode(typeToken: tokens[0]), pointerToken: tokens[1]), identifierToken: tokens[2]) + ] + ) + ) + } } From 79555a2951cd3f7fed588054e200d31f018603ff Mon Sep 17 00:00:00 2001 From: kntkymt Date: Sat, 16 Dec 2023 00:49:19 +0900 Subject: [PATCH 2/3] Support globalVariable on toplevel --- Sources/Generator/Generator.swift | 24 +++++++++++++++++++++--- test_exectable.sh | 1 + 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/Sources/Generator/Generator.swift b/Sources/Generator/Generator.swift index 08a2934..be0e4dc 100644 --- a/Sources/Generator/Generator.swift +++ b/Sources/Generator/Generator.swift @@ -30,6 +30,7 @@ public final class Generator { var addressOffset: Int } + private var globalVariables: [String: any TypeNodeProtocol] = [:] private var variables: [String: VariableInfo] = [:] private var functionLabels: Set = Set() @@ -39,14 +40,31 @@ public final class Generator { // MARK: - Public public func generate(sourceFileNode: SourceFileNode) throws -> String { - var result = "" + + var variableDeclResult = "" + for variableDecl in sourceFileNode.globalVariables { + variableDeclResult += try generateGlobalVariable(node: variableDecl) + } + + var functionDeclResult = "" for functionDecl in sourceFileNode.functions { - result += try generate(node: functionDecl) + functionDeclResult += try generate(node: functionDecl) } let functionMeta = ".globl \(functionLabels.joined(separator: ", "))\n" - return functionMeta + result + return variableDeclResult + functionMeta + functionDeclResult + } + + func generateGlobalVariable(node: VariableDeclNode) throws -> String { + var result = "" + + result += "\(node.identifierName):\n" + result += " .zero \(node.type.memorySize)\n" + + globalVariables[node.identifierName] = node.type + + return result } func generate(node: any NodeProtocol) throws -> String { diff --git a/test_exectable.sh b/test_exectable.sh index 23ae58d..bb4f400 100755 --- a/test_exectable.sh +++ b/test_exectable.sh @@ -79,5 +79,6 @@ assert 17 "int main() { int a[3]; int b; b = 1; a[b] = 7; a[b+1] = 10; return a[ assert 5 "int main() { int a[2]; a[1] = 5; int* p; p = a; return p[1]; }" assert 5 "int main() { int a[2]; a[1] = 5; return *(a+1); }" assert 1 "int main() { int a[2]; a[1] = 5; int* p; p = a; return p[1] == *(p+1); }" +assert 1 "int g; int main() { return 1; }" echo OK From 40f8f816739d1ff8f1b23448bcf88e205f4b9476 Mon Sep 17 00:00:00 2001 From: kntkymt Date: Sat, 16 Dec 2023 15:29:11 +0900 Subject: [PATCH 3/3] Support using global variable --- Sources/Generator/Generator.swift | 33 ++++++++++++++++++------------- test_exectable.sh | 5 ++++- 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/Sources/Generator/Generator.swift b/Sources/Generator/Generator.swift index be0e4dc..9e865c1 100644 --- a/Sources/Generator/Generator.swift +++ b/Sources/Generator/Generator.swift @@ -43,7 +43,7 @@ public final class Generator { var variableDeclResult = "" for variableDecl in sourceFileNode.globalVariables { - variableDeclResult += try generateGlobalVariable(node: variableDecl) + variableDeclResult += try generateGlobalVariableDecl(node: variableDecl) } var functionDeclResult = "" @@ -56,11 +56,11 @@ public final class Generator { return variableDeclResult + functionMeta + functionDeclResult } - func generateGlobalVariable(node: VariableDeclNode) throws -> String { + func generateGlobalVariableDecl(node: VariableDeclNode) throws -> String { var result = "" - result += "\(node.identifierName):\n" - result += " .zero \(node.type.memorySize)\n" + // g: .zeroじゃApple Clangは動かない? + result += ".comm g,\(node.type.memorySize),8\n" globalVariables[node.identifierName] = node.type @@ -82,7 +82,7 @@ public final class Generator { let casted = try node.casted(IdentifierNode.self) // アドレスをpush - result += try generatePushLocalVariableAddress(node: casted) + result += try generatePushVariableAddress(node: casted) // 配列の場合は先頭アドレスのまま返す // 配列以外の場合はアドレスの中身を返す @@ -175,7 +175,7 @@ public final class Generator { case .subscriptCallExpr: let casted = try node.casted(SubscriptCallExpressionNode.self) - result += try generatePushLocalArrayElementAddress(node: casted) + result += try generatePushArrayElementAddress(node: casted) // 結果のアドレスの値をロードしてスタックに積む result += " ldr x0, [sp]\n" @@ -341,7 +341,7 @@ public final class Generator { case .address: // &のあとは変数しか入らない(はず?) if let right = casted.right as? IdentifierNode { - result += try generatePushLocalVariableAddress(node: right) + result += try generatePushVariableAddress(node: right) } else { throw GenerateError.invalidSyntax(index: node.sourceTokens[0].sourceIndex) } @@ -355,11 +355,11 @@ public final class Generator { if casted.operator is AssignNode { // 左辺は変数, `*値`, subscriptCall if casted.left is IdentifierNode { - result += try generatePushLocalVariableAddress(node: casted.left.casted(IdentifierNode.self)) + result += try generatePushVariableAddress(node: casted.left.casted(IdentifierNode.self)) } else if let pointer = casted.left as? PrefixOperatorExpressionNode, pointer.operatorKind == .reference { result += try generate(node: pointer.right) } else if let subscriptCall = casted.left as? SubscriptCallExpressionNode { - result += try generatePushLocalArrayElementAddress(node: subscriptCall) + result += try generatePushArrayElementAddress(node: subscriptCall) } else { throw GenerateError.invalidSyntax(index: casted.left.sourceTokens[0].sourceIndex) } @@ -460,24 +460,29 @@ public final class Generator { // MARK: - Private /// nameの変数のアドレスをスタックにpushするコードを生成する - private func generatePushLocalVariableAddress(node: IdentifierNode) throws -> String { + private func generatePushVariableAddress(node: IdentifierNode) throws -> String { var result = "" - guard let offset = variables[node.identifierName]?.addressOffset else { + if let localVariableInfo = variables[node.identifierName] { + result += " sub x0, x29, #\(localVariableInfo.addressOffset)\n" + } else if globalVariables[node.identifierName] != nil { + // addじゃなくてldrであってる? + result += " adrp x0, \(node.identifierName)@GOTPAGE\n" + result += " ldr x0, [x0, \(node.identifierName)@GOTPAGEOFF]\n" + } else { throw GenerateError.noSuchVariable(varibaleName: node.identifierName, index: node.token.sourceIndex) } - result += " sub x0, x29, #\(offset)\n" result += " str x0, [sp, #-16]!\n" return result } - private func generatePushLocalArrayElementAddress(node: SubscriptCallExpressionNode) throws -> String { + private func generatePushArrayElementAddress(node: SubscriptCallExpressionNode) throws -> String { var result = "" // 配列の先頭アドレス, subscriptの値をpush - result += try generatePushLocalVariableAddress(node: node.identifierNode) + result += try generatePushVariableAddress(node: node.identifierNode) result += try generate(node: node.argument) result += " ldr x0, [sp]\n" diff --git a/test_exectable.sh b/test_exectable.sh index bb4f400..48d2066 100755 --- a/test_exectable.sh +++ b/test_exectable.sh @@ -79,6 +79,9 @@ assert 17 "int main() { int a[3]; int b; b = 1; a[b] = 7; a[b+1] = 10; return a[ assert 5 "int main() { int a[2]; a[1] = 5; int* p; p = a; return p[1]; }" assert 5 "int main() { int a[2]; a[1] = 5; return *(a+1); }" assert 1 "int main() { int a[2]; a[1] = 5; int* p; p = a; return p[1] == *(p+1); }" -assert 1 "int g; int main() { return 1; }" +assert 10 "int g; int main() { g = 10; return g; }" +assert 100 "int g; int main() { g = 10; return g * 10; }" +assert 30 "int g; int sub() { g = 30; return 0; } int main() { g = 20; sub(); return g; }" +assert 30 "int g[10]; int main() { g[2] = 10; g[3] = 20; return g[2] + g[2 + 1]; }" echo OK