Skip to content

Commit

Permalink
Extracted demo into example language frontend
Browse files Browse the repository at this point in the history
  • Loading branch information
oxisto committed Nov 30, 2024
1 parent f88a81a commit bea15ba
Show file tree
Hide file tree
Showing 4 changed files with 227 additions and 41 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,12 @@ import org.slf4j.LoggerFactory
* after having processed the files, i.e., it won't be available in passes.
*
* More information can be found in the
* [GitHub wiki page](https://github.com/Fraunhofer-AISEC/cpg/wiki/Language-Frontends).
* [Documentation](https://fraunhofer-aisec.github.io/cpg/CPG/impl/language/#languagefrontend).
*
* @param AstNode This type parameter should be set to a class that is common to all AST nodes in
* the language.
* @param TypeNode This type parameter should be set to a class that is common to all type nodes in
* the language.
*/
abstract class LanguageFrontend<AstNode, TypeNode>(
/** The language this frontend works for. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,52 +25,20 @@
*/
package de.fraunhofer.aisec.cpg

import de.fraunhofer.aisec.cpg.frontends.Handler
import de.fraunhofer.aisec.cpg.example.ExampleLanguageFrontend
import de.fraunhofer.aisec.cpg.example.RawFileNode
import de.fraunhofer.aisec.cpg.example.RawFunctionNode
import de.fraunhofer.aisec.cpg.example.RawParameterNode
import de.fraunhofer.aisec.cpg.example.RawTypeNode
import de.fraunhofer.aisec.cpg.frontends.TestLanguageFrontend
import de.fraunhofer.aisec.cpg.graph.*
import de.fraunhofer.aisec.cpg.graph.declarations.Declaration
import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration
import de.fraunhofer.aisec.cpg.graph.declarations.ProblemDeclaration
import de.fraunhofer.aisec.cpg.graph.declarations.TranslationUnitDeclaration
import de.fraunhofer.aisec.cpg.graph.plusAssign
import java.io.File
import kotlin.test.Test
import kotlin.test.assertIs
import kotlin.test.assertNotNull

class RawFileDeclarationNode(var children: List<RawDeclarationNode>) : RawDeclarationNode()

class RawFunctionDeclarationNode : RawDeclarationNode()

open class RawDeclarationNode : RawNode()

open class RawNode

private class TestDeclarationHandler(frontend: TestLanguageFrontend) :
Handler<Declaration, RawDeclarationNode, TestLanguageFrontend>(::ProblemDeclaration, frontend) {
override fun handle(node: RawDeclarationNode): Declaration {
when (node) {
is RawFunctionDeclarationNode -> handleFunction(node)
is RawFileDeclarationNode -> handleFile(node)
}

return ProblemDeclaration()
}

private fun handleFile(node: RawFileDeclarationNode): TranslationUnitDeclaration {
return translationUnitDeclaration("test.file").globalScope {
for (child in node.children) {
this += handle(child)
}
}
}

private fun handleFunction(node: RawFunctionDeclarationNode): FunctionDeclaration {
return functionDeclaration("main", rawNode = node).withScope {
val param = parameterDeclaration("argc", objectType("int"))

this += param
}
}
}

class NodeBuilderV2Test {
@Test
fun testBuild() {
Expand All @@ -97,4 +65,26 @@ class NodeBuilderV2Test {
val param = func.parameters["argc"]
assertNotNull(param)
}

@Test
fun testFrontend() {
val file =
RawFileNode(
"file.example",
children =
listOf(
RawFunctionNode(
"main",
params = listOf(RawParameterNode("argc", type = RawTypeNode("int")))
)
)
)

val frontend = ExampleLanguageFrontend(rawFileNode = file)
val node = frontend.parse(File(""))
assertIs<TranslationUnitDeclaration>(node)

val main = node.functions["main"]
assertNotNull(main)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
/*
* Copyright (c) 2024, Fraunhofer AISEC. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* $$$$$$\ $$$$$$$\ $$$$$$\
* $$ __$$\ $$ __$$\ $$ __$$\
* $$ / \__|$$ | $$ |$$ / \__|
* $$ | $$$$$$$ |$$ |$$$$\
* $$ | $$ ____/ $$ |\_$$ |
* $$ | $$\ $$ | $$ | $$ |
* \$$$$$ |$$ | \$$$$$ |
* \______/ \__| \______/
*
*/
package de.fraunhofer.aisec.cpg.example

import de.fraunhofer.aisec.cpg.ScopeManager
import de.fraunhofer.aisec.cpg.TranslationConfiguration
import de.fraunhofer.aisec.cpg.TranslationContext
import de.fraunhofer.aisec.cpg.TypeManager
import de.fraunhofer.aisec.cpg.frontends.Handler
import de.fraunhofer.aisec.cpg.frontends.HasClasses
import de.fraunhofer.aisec.cpg.frontends.Language
import de.fraunhofer.aisec.cpg.frontends.LanguageFrontend
import de.fraunhofer.aisec.cpg.graph.Node
import de.fraunhofer.aisec.cpg.graph.declarations.Declaration
import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration
import de.fraunhofer.aisec.cpg.graph.declarations.ParameterDeclaration
import de.fraunhofer.aisec.cpg.graph.declarations.ProblemDeclaration
import de.fraunhofer.aisec.cpg.graph.declarations.TranslationUnitDeclaration
import de.fraunhofer.aisec.cpg.graph.functionDeclaration
import de.fraunhofer.aisec.cpg.graph.globalScope
import de.fraunhofer.aisec.cpg.graph.objectType
import de.fraunhofer.aisec.cpg.graph.parameterDeclaration
import de.fraunhofer.aisec.cpg.graph.plusAssign
import de.fraunhofer.aisec.cpg.graph.translationUnitDeclaration
import de.fraunhofer.aisec.cpg.graph.types.IntegerType
import de.fraunhofer.aisec.cpg.graph.types.Type
import de.fraunhofer.aisec.cpg.graph.withScope
import de.fraunhofer.aisec.cpg.sarif.PhysicalLocation
import java.io.File
import kotlin.reflect.KClass

/**
* This is an example language, that demonstrate how to construct a [Language]. Our language has the
* following features:
* - It uses the `.example` file extension
* - It has classes and namespaces
* - Namespaces are separated by a `::` delimiter
* - It features some built-in types, such as `int`
*/
class ExampleLanguage : Language<ExampleLanguageFrontend>(), HasClasses {
override val fileExtensions: List<String> = listOf("example")
override val namespaceDelimiter: String = "::"
override val frontend: KClass<out ExampleLanguageFrontend> = ExampleLanguageFrontend::class
override val builtInTypes: Map<String, Type> = mapOf("int" to IntegerType())
override val compoundAssignmentOperators = setOf<String>()
}

/**
* This is an example language frontend, that demonstrates how to translate languages into the CPG.
* In this case, we want to translate our [ExampleLanguage]. It takes nodes that derive from
* [RawNode] -- these would come from a real parser in a real language -- and translates them into a
* CPG [Node].
*/
class ExampleLanguageFrontend(
language: ExampleLanguage = ExampleLanguage(),
ctx: TranslationContext =
TranslationContext(
TranslationConfiguration.builder().build(),
ScopeManager(),
TypeManager()
),
var rawFileNode: RawFileNode
) : LanguageFrontend<RawNode, RawTypeNode>(language, ctx) {

/**
* We want to store an instance of our [Handler] here, in this case one that takes care of a
* [Declaration].
*/
private var declarationHandler = ExampleDeclarationHandler(this)

override fun parse(file: File): TranslationUnitDeclaration {
// In a real language frontend, we would read the AST from the contents of the file, but
// since we are not fully implementing a parser, we are using the rawFileNode from our
// constructor instead.
val parsedFile = rawFileNode

return translationUnitDeclaration(parsedFile.name).globalScope {
for (child in parsedFile.children) {
this += declarationHandler.handle(child)
}
}
}

override fun typeOf(type: RawTypeNode): Type {
return objectType(type.name)
}

override fun codeOf(astNode: RawNode): String? {
return astNode.code
}

override fun locationOf(astNode: RawNode): PhysicalLocation? {
return null
}

override fun setComment(node: Node, astNode: RawNode) {}
}

class ExampleDeclarationHandler(frontend: ExampleLanguageFrontend) :
Handler<Declaration, RawDeclarationNode, ExampleLanguageFrontend>(
::ProblemDeclaration,
frontend
) {
override fun handle(node: RawDeclarationNode): Declaration {
return when (node) {
is RawFunctionNode -> handleFunction(node)
is RawParameterNode -> handleParameter(node)
else -> ProblemDeclaration()
}
}

private fun handleFunction(node: RawFunctionNode): FunctionDeclaration {
return functionDeclaration(node.name, rawNode = node).withScope {
for (param in node.params) {
val decl = handle(param)

this += decl
}
}
}

private fun handleParameter(node: RawParameterNode): ParameterDeclaration {
return parameterDeclaration(node.name, type = frontend.typeOf(node.type))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright (c) 2024, Fraunhofer AISEC. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* $$$$$$\ $$$$$$$\ $$$$$$\
* $$ __$$\ $$ __$$\ $$ __$$\
* $$ / \__|$$ | $$ |$$ / \__|
* $$ | $$$$$$$ |$$ |$$$$\
* $$ | $$ ____/ $$ |\_$$ |
* $$ | $$\ $$ | $$ | $$ |
* \$$$$$ |$$ | \$$$$$ |
* \______/ \__| \______/
*
*/
package de.fraunhofer.aisec.cpg.example

class RawFileNode(name: String, val children: List<RawDeclarationNode>) : RawDeclarationNode(name)

class RawFunctionNode(name: String, val params: List<RawParameterNode>) : RawDeclarationNode(name)

class RawParameterNode(name: String, val type: RawTypeNode) : RawDeclarationNode(name)

open class RawDeclarationNode(val name: String) : RawNode()

/** This node represents some sort of type in the example language. */
open class RawTypeNode(val name: String)

/**
* This node represents an in-memory object of a raw node of our example language that a parser
* would create. It is the base node for all other classes.
*/
open class RawNode(val code: String? = null)

0 comments on commit bea15ba

Please sign in to comment.