diff --git a/.obsidian/workspace.json b/.obsidian/workspace.json index 0ca0da4..e5ed97f 100644 --- a/.obsidian/workspace.json +++ b/.obsidian/workspace.json @@ -18,8 +18,19 @@ "source": false } } + }, + { + "id": "707df617f88c25ee", + "type": "leaf", + "state": { + "type": "release-notes", + "state": { + "currentVersion": "1.4.16" + } + } } - ] + ], + "currentTab": 1 } ], "direction": "vertical" @@ -156,7 +167,7 @@ "command-palette:Open command palette": false } }, - "active": "33dc0471a61fef18", + "active": "707df617f88c25ee", "lastOpenFiles": [ "test/t2.c", "test/t1 copy.c", diff --git a/CHANGELOG.md b/CHANGELOG.md index 97b93cb..0db8f93 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,14 +1,25 @@ # CZECHTINA - CHANGELOG +### v 0.1.6 + +- throw keyword +- variables now has to start with lowercase letter +- functions now has to start with lowercase letter +- types now has to start with uppercase letter +- structures in work +- while loop +- `-1` is now valid +- undefining functions via `#undefine ` + ### v 0.1.5 -- function parameters can be casted to const via `@` +- function parameters can be cast to const via `@` - function can take entire heap memory via `&` it will automatically deallocate memory - templating via T keyword (T is replaced by type, available T - T999999999999999 types) - function overloading - `as` or `jako` keyword for type casting - `new` virtual function for allocating memory -- automatical deallocation of memory (weak - garbage collector) +- automatically deallocation of memory (weak - garbage collector) - preprocessor - file structure - including file (before compilation) diff --git a/README.md b/README.md index d744b12..0d8756b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Czechtina -Current version: 0.1.5 +Current version: 0.1.6 Czechtina is programming language based on C and czech language. Czechtina should be faster to write than c and with additional features, easier to maintain. @@ -235,7 +235,9 @@ To get address of variable use virtual Function `adresa` ## Building compiler -TODO - use intelij xd +```bash +./gradlew build +``` ## Using Compiler @@ -248,6 +250,7 @@ java -jar czechtina.jar build/ukol.cz --no-compile --fpeterek --friendly --set-d - **--no-compile** - Do not compile the output C code, it will be created in the same directory as the input file - **--show-tree** - Show the AST tree - **--write-code** - Write Code in comment before C code +- **--debug** - Show debug info - **--fpeterek** - Uses macros from old czechtina.h file - **--friendly** - Generate valid C without macros in comment bellow code - **--set-dir** - Set dir for file creation @@ -256,11 +259,12 @@ java -jar czechtina.jar build/ukol.cz --no-compile --fpeterek --friendly --set-d - std lib - string anotation -- better cli - file structure - rest of loop features. -- structures +- nacti function +- objects - range definition +- pointer array borrow checking - check validation of return type ## Credits diff --git a/build.gradle.kts b/build.gradle.kts index 159a091..c0fe848 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -19,8 +19,8 @@ dependencies { implementation("org.json:json:20230618") testImplementation(kotlin("test")) - implementation("cz.j_jzk.klang:klang:1.0-rc2") - implementation("cz.j_jzk.klang:klang-prales:1.0-rc2") + implementation("cz.j_jzk.klang:klang:1.0-rc3") + implementation("cz.j_jzk.klang:klang-prales:1.0-rc3") } tasks.test { diff --git a/src/main/kotlin/AST/ASTArrayAccessNode.kt b/src/main/kotlin/AST/ASTArrayAccessNode.kt index bbea745..b550552 100644 --- a/src/main/kotlin/AST/ASTArrayAccessNode.kt +++ b/src/main/kotlin/AST/ASTArrayAccessNode.kt @@ -12,7 +12,7 @@ class ASTArrayAccessNode: ASTVariableNode { } override fun getType(): DefinedType { - return DefinedType( array.getType().typeString.split("-")[1], array.getType().isHeap, array.getType().isConst) + return array.getType().toDereference() } override fun copy(): ASTArrayAccessNode { @@ -27,7 +27,7 @@ class ASTArrayAccessNode: ASTVariableNode { return "ARRAY ACCESS, \narray=${array.toString().replace("\n","\n\t")}, \nindex=${index.toString().replace("\n","\n\t")}\n" } - override fun toC(): String { - return "${array.toC()}[${index.toC()}]" + override fun toC(sideEffect:Boolean): String { + return "${array.toC(false)}[${index.toC(false)}]" } } \ No newline at end of file diff --git a/src/main/kotlin/AST/ASTBinaryNode.kt b/src/main/kotlin/AST/ASTBinaryNode.kt index 3f54a61..e7ac3e8 100644 --- a/src/main/kotlin/AST/ASTBinaryNode.kt +++ b/src/main/kotlin/AST/ASTBinaryNode.kt @@ -2,8 +2,7 @@ package AST import compiler.Compiler import compiler.DefinedType -import czechtina.GrammarToken -import czechtina.czechtina +import czechtina.grammar.GrammarToken enum class ASTBinaryTypes { @@ -18,7 +17,7 @@ open class ASTBinaryNode : ASTNode { - constructor(type:ASTBinaryTypes,left:ASTNode, right:ASTNode) { + constructor(type:ASTBinaryTypes,left:ASTNode, right:ASTNode) : super(DefinedType("none")) { this.type = type this.left = left this.right = right @@ -37,7 +36,7 @@ open class ASTBinaryNode : ASTNode { return "'$type', \nleft=${left.toString().replace("\n","\n\t")}, \nright=${right.toString().replace("\n","\n\t")}\n" } - override fun toC(): String = when (type) { + override fun toC(sideEffect:Boolean): String = when (type) { ASTBinaryTypes.FLOW_CONTROL -> "${left.toC()} ${right.toC()}" ASTBinaryTypes.TYPE_DEFINITION -> "${Compiler.grammar[GrammarToken.KEYWORD_TYPE_DEFINITION]} ${left.toC()} ${right.toC()};" } diff --git a/src/main/kotlin/AST/ASTForNode.kt b/src/main/kotlin/AST/ASTForNode.kt index 8a192e7..3d5034d 100644 --- a/src/main/kotlin/AST/ASTForNode.kt +++ b/src/main/kotlin/AST/ASTForNode.kt @@ -2,8 +2,8 @@ package AST import compiler.Compiler import compiler.DefinedType -import czechtina.GrammarToken -import czechtina.czechtina +import czechtina.grammar.GrammarToken +import czechtina.grammar.czechtina class ASTForNode : ASTNode { var begin: ASTNode @@ -11,7 +11,7 @@ class ASTForNode : ASTNode { var step: ASTNode var body: ASTNode - constructor(begin: ASTNode, condition: ASTNode, step: ASTNode, body: ASTNode) { + constructor(begin: ASTNode, condition: ASTNode, step: ASTNode, body: ASTNode) : super(DefinedType("")) { this.begin = begin this.condition = condition this.step = step @@ -19,7 +19,7 @@ class ASTForNode : ASTNode { unscopeBody() } - constructor(variable: ASTVariableNode, type: ASTUnaryNode, min: ASTTypedNode, rangeComparation: String , max: ASTTypedNode, body: ASTNode) { + constructor(variable: ASTVariableNode, type: ASTUnaryNode, min: ASTNode, rangeComparation: String , max: ASTNode, body: ASTNode) : super(DefinedType("")) { this.begin = ASTUnaryNode(ASTUnaryTypes.SEMICOLON,ASTOperandNode(czechtina[GrammarToken.OPERATOR_ASSIGN]!!, variable.addType(type.getType()), min) ) @@ -57,5 +57,5 @@ class ASTForNode : ASTNode { return "For: \nbegin=${begin.toString().replace("\n","\n\t")}, \ncondition=${condition.toString().replace("\n","\n\t")}, \nstep=${step.toString().replace("\n","\n\t")}, \nbody=${body.toString().replace("\n","\n\t")}" } - override fun toC() = "for ${Compiler.scopePush()} (${begin.toC()} ${condition.toC()}; ${step.toC()}) ${body.toC()}" + override fun toC(sideEffect:Boolean) = "for ${Compiler.scopePush()} (${begin.toC()} ${condition.toC()}; ${step.toC()}) ${body.toC()}" } \ No newline at end of file diff --git a/src/main/kotlin/AST/ASTFunctionCallNode.kt b/src/main/kotlin/AST/ASTFunctionCallNode.kt index a360122..30823b2 100644 --- a/src/main/kotlin/AST/ASTFunctionCallNode.kt +++ b/src/main/kotlin/AST/ASTFunctionCallNode.kt @@ -2,33 +2,45 @@ package AST import compiler.Compiler import compiler.DefinedType -import czechtina.GrammarToken -import czechtina.czechtina +import czechtina.grammar.GrammarToken +import czechtina.grammar.czechtina class ASTFunctionCallNode : ASTVariableNode { - var function:ASTTypedNode + var function:ASTVariableNode var params:ASTNode? = null - constructor(function:ASTTypedNode, params:ASTNode? = null): super("", DefinedType("none")) { + constructor(function:ASTVariableNode, params:ASTNode? = null): super("", DefinedType("none")) { this.function = function this.params = params } override fun getType(): DefinedType { + if (function.data == "throw") + return DefinedType("none") + if (function.data == "inC") + return DefinedType("none") + if (function.data == "predej") + return (params!! as ASTVariableNode).getType().toDynamic() + if (function.data == "hodnota") + return params!!.getType().toDereference() + if (function.data == "adresa") + return params!!.getType().toPointer() + if (function.data == "const") + return (params!! as ASTVariableNode).getType().toConst() + + if (function.data == "new" && (params as ASTNode).getType().isStructured) + return (params as ASTNode).getType().toDynamic() + if (Compiler.definedFunctions.containsKey(function.toC())) { val paramsTypes = mutableListOf() if (params is ASTListNode) for (param in (params as ASTListNode).nodes) paramsTypes.add(param.getType()) - else if (params is ASTTypedNode) - paramsTypes.add((params as ASTTypedNode).getType()) + else if (params is ASTNode) + paramsTypes.add((params as ASTNode).getType()) - if (function.toC() == "predej") - return (params!! as ASTVariableNode).getType().toDynamic() - if (function.toC() == "const") - return (params!! as ASTVariableNode).getType().toConst() val variantIndex = Compiler.definedFunctions[function.toC()]!!.validateParams(paramsTypes) if (variantIndex != -1) return Compiler.definedFunctions[function.toC()]!!.getReturnType(variantIndex) @@ -49,19 +61,32 @@ class ASTFunctionCallNode : ASTVariableNode { return ASTFunctionCallNode(function.copy(), params?.copy()) } - override fun toC(): String { - if (function?.toC().equals(czechtina[GrammarToken.TYPE_ADDRESS]!!)) + override fun toC(sideEffect:Boolean): String { + + if (function.data == "throw") + return "printf(${params?.toC()}); exit(1)" + + if (function.data == "inC") + return "${(params as ASTUnaryNode).data}" + + if (function.data.equals(czechtina[GrammarToken.TYPE_ADDRESS]!!)) return "&${params?.toC()}" - if (function?.toC().equals(czechtina[GrammarToken.TYPE_VALUE]!!)) + if (function.data.equals(czechtina[GrammarToken.TYPE_VALUE]!!)) return "*(${params?.toC()})" - if (function?.toC().equals("predej")) { + if (function.data.equals("new")){ + if ((params as ASTNode).getType().isStructured) { + return "(${(params as ASTNode).getType().toC()})malloc(sizeof(${(params as ASTNode).getType().getPrimitive()}))" + } + } + + if (function.data.equals("predej")) { val body = "${params?.toC()}" Compiler.variables[Compiler.variables.size-1][params?.toC()!!]!!.dealocated = true return body } - if (function?.toC().equals("const")) { + if (function.data.equals("const")) { if (params is ASTVariableNode){ if (!(params as ASTVariableNode).getType().isPointer()) throw Exception("Const can be applied only to objects") @@ -70,13 +95,13 @@ class ASTFunctionCallNode : ASTVariableNode { throw Exception("Const can be applied only to variables") } - if (Compiler.definedFunctions.containsKey(function.toC())) { + if (Compiler.definedFunctions.containsKey(function.data)) { val paramsTypes = mutableListOf() if (params is ASTListNode) for (param in (params as ASTListNode).nodes) paramsTypes.add(param.getType()) - else if (params is ASTTypedNode) - paramsTypes.add((params as ASTTypedNode).getType()) + else if (params is ASTNode) + paramsTypes.add((params as ASTNode).getType()) val variantIndex = Compiler.definedFunctions[function.toC()]!!.validateParams(paramsTypes) if (variantIndex != -1) { @@ -85,8 +110,8 @@ class ASTFunctionCallNode : ASTVariableNode { } throw Exception("Function ${function.toC()} with params ${paramsTypes.joinToString(",")} not found") } - + if (Compiler.undefinedFunction.contains(function.toC(false))) + return "${function.toC(false)}(${params?.toC()})" throw Exception("Function ${function.toC()} not found") - return "${function.toC()}(${params?.toC()})" } } \ No newline at end of file diff --git a/src/main/kotlin/AST/ASTFunctionNode.kt b/src/main/kotlin/AST/ASTFunctionNode.kt index 7dfe5f0..38ad926 100644 --- a/src/main/kotlin/AST/ASTFunctionNode.kt +++ b/src/main/kotlin/AST/ASTFunctionNode.kt @@ -9,7 +9,7 @@ class ASTFunctionNode : ASTNode { var parameters:List = listOf() var body:ASTUnaryNode? = null - constructor(type:ASTUnaryNode, name:String, parameters:List, body:ASTUnaryNode) { + constructor(type:ASTUnaryNode, name:String, parameters:List, body:ASTUnaryNode) : super(DefinedType("")) { this.type = type this.name = name this.parameters = parameters @@ -34,11 +34,10 @@ class ASTFunctionNode : ASTNode { return "Function '$name' : '$type', \nparameters=${parameters.joinToString("").replace("\n","\n\t")}, \nbody=${body.toString().replace("\n","\n\t")}" } - override fun toC(): String { + override fun toC(sideEffect:Boolean): String { val paramsTypes = mutableListOf() for (param in parameters) - if (param is ASTTypedNode) - paramsTypes.add(param.getType()) + paramsTypes.add(param.getType()) if (paramsTypes.any{it.isTemplate()}) return "//${name}_CZECHTINA ANCHOR\n" return "//${name}_CZECHTINA ANCHOR\n${type.toC()}${Compiler.scopePush()} ${name}(${parameters.joinToString(", ") { it.toC() }}) ${body?.toC()}" @@ -48,6 +47,17 @@ class ASTFunctionNode : ASTNode { return ASTFunctionNode(type.copy(), name!!, parameters.map { it.copy() }, body!!.copy()) } + fun precalculateType() { + if (type.expType.typeString != "none") + return + + Compiler.scopePush() + for (param in parameters) + Compiler.setNewVariableType((param as ASTVarDefinitionNode).variable.data, param.type.getType()) + type.expType = body!!.getType() + Compiler.scopePop(false) + } + fun toCNoSideEffect(): String = "${type.toC()}${Compiler.scopePush()} ${name}(${parameters.joinToString(", ") { it.toC() }}) ${body?.toC()}" @@ -56,8 +66,7 @@ class ASTFunctionNode : ASTNode { fun toCDeclaration(): String { val paramsTypes = mutableListOf() for (param in parameters) - if (param is ASTTypedNode) - paramsTypes.add(param.getType()) + paramsTypes.add(param.getType()) if (Compiler.definedFunctions.containsKey(name!!)) { val newName = "${name}_v${Compiler.definedFunctions[name!!]!!.variants.size}" diff --git a/src/main/kotlin/AST/ASTListNode.kt b/src/main/kotlin/AST/ASTListNode.kt index 2951824..5e88c37 100644 --- a/src/main/kotlin/AST/ASTListNode.kt +++ b/src/main/kotlin/AST/ASTListNode.kt @@ -3,20 +3,20 @@ package AST import compiler.DefinedType class ASTListNode : ASTNode { - val nodes: List + val nodes: List - constructor(nodes: List) { + constructor(nodes: List) : super(DefinedType("")) { this.nodes = nodes } - fun getType(): DefinedType { + override fun getType(): DefinedType { val types = nodes.map { it.getType() } val type = types[0] for (t in types) { - if (t != type) - throw Exception("Cant get type of array of variant types") + if (t.typeString != type.typeString) + throw Exception("Cant get type of array of variant types, $type and $t") } - return type; + return type.toArray(types.size.toString()); } override fun toString(): String { @@ -33,6 +33,6 @@ class ASTListNode : ASTNode { } - override fun toC(): String = nodes.joinToString(",") { it.toC() } + override fun toC(sideEffect:Boolean): String = nodes.joinToString(",") { it.toC() } } \ No newline at end of file diff --git a/src/main/kotlin/AST/ASTNode.kt b/src/main/kotlin/AST/ASTNode.kt index aca8fbe..9f53580 100644 --- a/src/main/kotlin/AST/ASTNode.kt +++ b/src/main/kotlin/AST/ASTNode.kt @@ -1,13 +1,19 @@ package AST import compiler.DefinedType -import czechtina.GrammarToken -open abstract class ASTNode { +open abstract class ASTNode(var expType: DefinedType) { - abstract fun toC(): String + abstract fun toC(sideEffect:Boolean = true): String abstract fun copy(): ASTNode - abstract fun retype(map: Map) + open fun getType(): DefinedType = expType + + open fun retype(map: Map) { + for (m in map) + if (expType.typeString == m.key) + expType = m.value + } + } diff --git a/src/main/kotlin/AST/ASTOperandNode.kt b/src/main/kotlin/AST/ASTOperandNode.kt index 83c79a3..c8cf1d7 100644 --- a/src/main/kotlin/AST/ASTOperandNode.kt +++ b/src/main/kotlin/AST/ASTOperandNode.kt @@ -2,19 +2,22 @@ package AST import compiler.Compiler import compiler.DefinedType -import czechtina.cTypeFromCzechtina -class ASTOperandNode : ASTTypedNode { +class ASTOperandNode : ASTNode { var operand:String - var left: ASTTypedNode - var right: ASTTypedNode + var left: ASTNode + var right: ASTNode - constructor(operand:String, left:ASTTypedNode, right:ASTTypedNode) : super(Compiler.calcBinaryType(left, right, operand)) { + constructor(operand:String, left:ASTNode, right:ASTNode) : super(Compiler.calcBinaryType(left, right, operand)) { this.operand = operand this.left = left this.right = right } + override fun getType(): DefinedType { + return Compiler.calcBinaryType(left, right, operand) + } + override fun retype(map: Map) { left.retype(map) right.retype(map) @@ -29,7 +32,13 @@ class ASTOperandNode : ASTTypedNode { return ASTOperandNode(operand, left.copy(), right.copy()) } - override fun toC(): String { + override fun toC(sideEffect:Boolean): String { + if (operand == "="){ + val rString = right?.toC() + expType = Compiler.calcBinaryType(left, right, operand) + return "${left?.toC()} = $rString" + } + expType = Compiler.calcBinaryType(left, right, operand) return "${left?.toC()} ${Compiler.typeFromCzechtina(operand)} ${right?.toC()}" } diff --git a/src/main/kotlin/AST/ASTProgramLines.kt b/src/main/kotlin/AST/ASTProgramLines.kt index b170ce5..b0f3f3e 100644 --- a/src/main/kotlin/AST/ASTProgramLines.kt +++ b/src/main/kotlin/AST/ASTProgramLines.kt @@ -5,7 +5,7 @@ import compiler.DefinedType class ASTProgramLines : ASTNode { var programLines: List = listOf() - constructor(programLines: List) { + constructor(programLines: List) : super(DefinedType("")) { this.programLines = programLines } @@ -24,5 +24,5 @@ class ASTProgramLines : ASTNode { programLines.forEach { it.retype(map) } } - override fun toC(): String = programLines.joinToString("\n") { it.toC() } + override fun toC(sideEffect:Boolean): String = programLines.joinToString("\n") { it.toC() } } \ No newline at end of file diff --git a/src/main/kotlin/AST/ASTProgramNode.kt b/src/main/kotlin/AST/ASTProgramNode.kt index 6384c71..3115f07 100644 --- a/src/main/kotlin/AST/ASTProgramNode.kt +++ b/src/main/kotlin/AST/ASTProgramNode.kt @@ -3,33 +3,40 @@ package AST import compiler.DefinedType class ASTProgramNode : ASTNode { - var imports: List = listOf() - var functions: List = listOf() + var imports: List = listOf() + var functions: List = listOf() + var structures: List = listOf() var typeDefinition = listOf() var main: ASTFunctionNode? = null - constructor(functions: List, imports: List, main: ASTFunctionNode?) { + constructor(functions: List, imports: List, main: ASTFunctionNode?): super(DefinedType("")) { this.imports = imports this.functions = functions this.main = main + structures = emptyList() } - public fun appendFunction(function: ASTFunctionNode): ASTProgramNode { + fun appendFunction(function: ASTFunctionNode): ASTProgramNode { functions += function return this } - public fun appendTypeDefinition(typeDefinition: ASTBinaryNode): ASTProgramNode { + fun appendTypeDefinition(typeDefinition: ASTBinaryNode): ASTProgramNode { this.typeDefinition += typeDefinition return this } - public fun appendImport(import: ASTUnaryNode): ASTProgramNode { + fun appendImport(import: ASTUnaryNode): ASTProgramNode { imports += import return this } + fun appendStructure(struct: ASTStructureNode): ASTProgramNode { + structures += struct + return this + } + override fun retype(map: Map) { functions.forEach { it.retype(map) } main?.retype(map) @@ -51,9 +58,10 @@ class ASTProgramNode : ASTNode { } fun doubleNewLine(condition:Boolean) = if (condition) "\n\n" else "" - override fun toC(): String = + override fun toC(sideEffect:Boolean): String = imports.joinToString("\n") { it.toC() } + - doubleNewLine(imports.isNotEmpty()) + functions.joinToString("\n") { it.toCDeclaration() } + + doubleNewLine(imports.isNotEmpty()) + structures.joinToString("\n") { it.toC() } + + doubleNewLine(structures.isNotEmpty()) + functions.joinToString("\n") { it.toCDeclaration() } + doubleNewLine(functions.isNotEmpty()) + typeDefinition.joinToString("\n") { it.toC() } + doubleNewLine(typeDefinition.isNotEmpty()) + functions.joinToString("\n\n") { it.toC() } + doubleNewLine(functions.isNotEmpty()) + main?.toC() diff --git a/src/main/kotlin/AST/ASTRetypeNode.kt b/src/main/kotlin/AST/ASTRetypeNode.kt index dc07f09..56a3918 100644 --- a/src/main/kotlin/AST/ASTRetypeNode.kt +++ b/src/main/kotlin/AST/ASTRetypeNode.kt @@ -2,28 +2,33 @@ package AST import compiler.DefinedType -class ASTRetypeNode: ASTTypedNode { - val expression: ASTTypedNode; - val type: ASTTypedNode; +class ASTRetypeNode: ASTNode { + val expression: ASTNode; + val type: ASTNode; - constructor(expression: ASTTypedNode, type: ASTTypedNode): super(DefinedType("")) { + constructor(expression: ASTNode, type: ASTNode): super(DefinedType("")) { this.expression = expression; this.type = type; } override fun getType(): DefinedType { - if (expression.getType().typeString.contains("-") && !type.getType().typeString.contains("-")) + if (expression.getType().typeString.contains("-") && !this.type.getType().typeString.contains("-")) throw Exception("Cannot retype pointer to non-pointer type") - if (type.getType().typeString.contains("arrau")) + if (this.type.getType().typeString.contains("array")) throw Exception("Cannot retype to array type") - var type = type.getType(); - if (expression.getType().isHeap) - type = type.toHeap() + var type = DefinedType(expression.getType()) + type.typeString = this.type.getType().typeString + if (expression.getType().isDynamic()) + type = type.toDynamic() return type } + override fun retype(map: Map) { + expression.retype(map) + type.retype(map) + } override fun copy(): ASTRetypeNode { return ASTRetypeNode(expression.copy(), type.copy()) } @@ -32,7 +37,7 @@ class ASTRetypeNode: ASTTypedNode { return "Retype: ${expression} to ${type}" } - override fun toC(): String { + override fun toC(sideEffect:Boolean): String { return "(${type.toC()})(${expression.toC()})" } } \ No newline at end of file diff --git a/src/main/kotlin/AST/ASTStaticArrayDefinitionNode.kt b/src/main/kotlin/AST/ASTStaticArrayDefinitionNode.kt index 5257af5..192d1b6 100644 --- a/src/main/kotlin/AST/ASTStaticArrayDefinitionNode.kt +++ b/src/main/kotlin/AST/ASTStaticArrayDefinitionNode.kt @@ -1,15 +1,13 @@ package AST +import compiler.Compiler import compiler.DefinedType -class ASTStaticArrayDefinitionNode : ASTTypedNode { - var type: ASTNode - var variable: ASTNode +class ASTStaticArrayDefinitionNode : ASTVarDefinitionNode { var size: String - constructor(type: ASTNode, variable: ASTNode, size: String): super(DefinedType("array-$type-$size")) { - this.type = type - this.variable = variable + constructor(type: ASTNode, variable: ASTVariableNode, size: String): super( variable,type) { + variable.addType(type.getType().toArray(size)) this.size = size } @@ -21,5 +19,5 @@ class ASTStaticArrayDefinitionNode : ASTTypedNode { return ASTStaticArrayDefinitionNode(type.copy(), variable.copy(), size) } - override fun toC(): String = "${type.toC()} ${variable.toC()}[${size}]" + override fun toC(sideEffect:Boolean): String = if (Compiler.controlDefinedVariables(variable.data)) variable.toDefineC() else "" } \ No newline at end of file diff --git a/src/main/kotlin/AST/ASTStructureAccessNode.kt b/src/main/kotlin/AST/ASTStructureAccessNode.kt new file mode 100644 index 0000000..13a7d64 --- /dev/null +++ b/src/main/kotlin/AST/ASTStructureAccessNode.kt @@ -0,0 +1,41 @@ +package AST + +import compiler.Compiler +import compiler.DefinedType + +class ASTStructureAccessNode: ASTVariableNode { + var struct:ASTVariableNode + var prop:ASTVariableNode + + constructor(struct:ASTNode, prop:ASTVariableNode) : super((struct as ASTVariableNode).data, DefinedType("")) { + this.struct = struct + this.prop = prop + } + + override fun getType(): DefinedType { + return Compiler.definedStructures[struct.getType().getPrimitive()]?.getPropType(prop.data) ?: DefinedType("none") + } + + override fun copy(): ASTStructureAccessNode { + return ASTStructureAccessNode(struct.copy(), prop.copy()) + } + override fun retype(map: Map) { + struct.retype(map) + prop.retype(map) + } + + override fun addType(type: DefinedType): ASTVariableNode { + prop.addType(type) + return this + } + + override fun toString(): String { + return "Struct ACCESS, \narray=${struct.toString().replace("\n","\n\t")}, \nindex=${prop.toString().replace("\n","\n\t")}\n" + } + + override fun toC(sideEffect:Boolean): String { + if (!struct.getType().isStructured) + throw Exception("Cant access to non struct type") + return "${struct.toC(false)}->${prop.toC(false)}" + } +} \ No newline at end of file diff --git a/src/main/kotlin/AST/ASTStructureNode.kt b/src/main/kotlin/AST/ASTStructureNode.kt new file mode 100644 index 0000000..a1f5e0d --- /dev/null +++ b/src/main/kotlin/AST/ASTStructureNode.kt @@ -0,0 +1,45 @@ +package AST + +import compiler.Compiler +import compiler.DefinedStructure +import compiler.DefinedType +import java.util.* + +class ASTStructureNode: ASTNode { + val name: String + val properties : MutableList + + constructor(name: String, props: List) : super(DefinedType("pointer-$name", isStructured = true, isHeap = true)) { + if (name.uppercase(Locale.getDefault()) != name) + throw Exception("Structure name must be in Uppercase") + this.name = name.uppercase(Locale.getDefault()) + this.properties = props.toMutableList() + } + + fun defineItSelf(): ASTStructureNode { + val structureType = DefinedType("pointer-$name", isStructured = true, isHeap = true) + Compiler.addToDefinedTypes(name, structureType) + Compiler.definedStructures += mapOf(name to + DefinedStructure(name, structureType, mutableMapOf()) + ) + return this + } + + fun addProperty(prop: ASTVarDefinitionNode):ASTStructureNode { + Compiler.definedStructures[name]!!.addPropType(prop.variable.data, prop.getType()) + properties.add(prop) + return this + } + + override fun toC(sideEffect:Boolean): String { + return "typedef struct ${Compiler.scopePush()}{\n\t${properties.joinToString(";\n\t") { it.toC() }};\n ${Compiler.scopePop(false)}} $name;" + } + + override fun retype(map: Map) { + properties.forEach{it.retype(map)} + } + + override fun copy(): ASTStructureNode { + return ASTStructureNode(name, properties.map { it.copy() }) + } +} \ No newline at end of file diff --git a/src/main/kotlin/AST/ASTTypedNode.kt b/src/main/kotlin/AST/ASTTypedNode.kt deleted file mode 100644 index e401439..0000000 --- a/src/main/kotlin/AST/ASTTypedNode.kt +++ /dev/null @@ -1,18 +0,0 @@ -package AST - -import compiler.DefinedType - - -abstract class ASTTypedNode(var expType: DefinedType) : ASTNode() { - - open fun getType(): DefinedType = expType - - abstract override fun copy(): ASTTypedNode - - override fun retype(map: Map) { - for (m in map) - if (expType.typeString == m.key) - expType = m.value - } - -} diff --git a/src/main/kotlin/AST/ASTUnaryNode.kt b/src/main/kotlin/AST/ASTUnaryNode.kt index 5463fde..a392f9c 100644 --- a/src/main/kotlin/AST/ASTUnaryNode.kt +++ b/src/main/kotlin/AST/ASTUnaryNode.kt @@ -2,13 +2,14 @@ package AST import compiler.Compiler import compiler.DefinedType -import czechtina.GrammarToken +import czechtina.grammar.GrammarToken enum class ASTUnaryTypes { LITERAL, VARIABLE, TYPE, TYPE_POINTER, + MINUS, RETURN, IMPORT, IMPORT_C, @@ -22,10 +23,11 @@ enum class ASTUnaryTypes { IF, ELSE, ELSE_IF, + WHILE, NEW_LINE, NO_PARAM_CALL } -class ASTUnaryNode : ASTTypedNode { +class ASTUnaryNode : ASTNode { var type:ASTUnaryTypes? = null var data:Any? = null @@ -34,6 +36,16 @@ class ASTUnaryNode : ASTTypedNode { this.data = data } + override fun getType(): DefinedType { + if (type == ASTUnaryTypes.TYPE) + return Compiler.tryGetDefinedType(data.toString()) ?: super.getType() + if (type == ASTUnaryTypes.TYPE_POINTER) + return expType + if (data is ASTNode) + return (data as ASTNode).getType() + return super.getType() + } + override fun toString(): String { return "'$type', data=$data, exp=${getType()}" @@ -55,7 +67,7 @@ class ASTUnaryNode : ASTTypedNode { override fun copy(): ASTUnaryNode { return ASTUnaryNode(type!!, data!!, expType) } - override fun toC(): String = when (type) { + override fun toC(sideEffect:Boolean): String = when (type) { ASTUnaryTypes.LITERAL -> data.toString() ASTUnaryTypes.VARIABLE -> data.toString() ASTUnaryTypes.TYPE -> if (getType().isTemplate()) getType().typeString else getType().toC() @@ -82,9 +94,11 @@ class ASTUnaryNode : ASTTypedNode { "{\n\t${body.replace("\n", "\n\t")}${Compiler.scopePop(true)}\n}" } ASTUnaryTypes.SEMICOLON -> "${(data as ASTNode).toC()};" + ASTUnaryTypes.MINUS -> "-${(data as ASTNode).toC()}" ASTUnaryTypes.JUST_C -> (data as ASTNode).toC() ASTUnaryTypes.STRING -> "\"${data.toString()}\"" ASTUnaryTypes.IF -> "${Compiler.grammar[GrammarToken.KEYWORD_IF]} (${(data as ASTNode).toC()})" + ASTUnaryTypes.WHILE -> "${Compiler.grammar[GrammarToken.KEYWORD_WHILE]} (${(data as ASTNode).toC()})" ASTUnaryTypes.ELSE -> "${Compiler.grammar[GrammarToken.KEYWORD_ELSE]}" ASTUnaryTypes.ELSE_IF -> "${Compiler.grammar[GrammarToken.KEYWORD_ELSE]} ${Compiler.grammar[GrammarToken.KEYWORD_IF]} (${(data as ASTNode).toC()})" ASTUnaryTypes.NEW_LINE -> "\n\t${(data as ASTNode).toC().replace("\n","\n\t")}" diff --git a/src/main/kotlin/AST/ASTVarDefinitionNode.kt b/src/main/kotlin/AST/ASTVarDefinitionNode.kt index 8c98e5c..51043f5 100644 --- a/src/main/kotlin/AST/ASTVarDefinitionNode.kt +++ b/src/main/kotlin/AST/ASTVarDefinitionNode.kt @@ -2,12 +2,10 @@ package AST import compiler.Compiler import compiler.DefinedType -import czechtina.GrammarToken -import czechtina.czechtina -class ASTVarDefinitionNode : ASTTypedNode { +open class ASTVarDefinitionNode : ASTNode { var variable:ASTVariableNode - var type:ASTTypedNode + var type:ASTNode override fun retype(map: Map) { @@ -18,21 +16,23 @@ class ASTVarDefinitionNode : ASTTypedNode { expType = m.value } + + override fun getType(): DefinedType { + return variable.getType() + } + override fun copy(): ASTVarDefinitionNode { return ASTVarDefinitionNode(variable.copy(), type.copy()) } - constructor(variable:ASTVariableNode, type : ASTTypedNode): super(type.expType) { + constructor(variable:ASTVariableNode, type : ASTNode): super(variable.expType) { this.type = type - this.variable = variable - - if (variable.isLocal) - Compiler.variables[Compiler.variables.size-1] += mapOf(variable.data to getType()) + this.variable = variable.addType(type.getType()) } override fun toString(): String { return "Var def for $variable with ${getType()}" } - override fun toC(): String = if (Compiler.controlDefinedVariables(variable.data)) variable.toDefineC() else "" + override fun toC(sideEffect:Boolean): String = if (Compiler.controlDefinedVariables(variable.data)) variable.toDefineC() else "" } \ No newline at end of file diff --git a/src/main/kotlin/AST/ASTVariableNode.kt b/src/main/kotlin/AST/ASTVariableNode.kt index e34a1da..7e3ee63 100644 --- a/src/main/kotlin/AST/ASTVariableNode.kt +++ b/src/main/kotlin/AST/ASTVariableNode.kt @@ -3,11 +3,12 @@ package AST import compiler.Compiler import compiler.DefinedType -open class ASTVariableNode : ASTTypedNode { +open class ASTVariableNode : ASTNode { val data: String val isLocal:Boolean constructor(data:String, expressionType: DefinedType, isLocal: Boolean = true) : super(expressionType) { + Compiler.setNewVariableType(data, DefinedType(expressionType)) this.data = data this.isLocal = isLocal @@ -23,16 +24,20 @@ open class ASTVariableNode : ASTTypedNode { return ASTVariableNode(data, expType, isLocal) } - fun addType(type: DefinedType ): ASTVariableNode { + open fun addType(type: DefinedType ): ASTVariableNode { + Compiler.setVariableType(data, DefinedType(type)) this.expType = type return this } override fun getType(): DefinedType { + val defined = Compiler.tryGetDefinedType(data) + if (defined != null) + return defined.unDynamic() val compType = Compiler.getVariableType(data) if (compType != null) - return compType - return super.getType() + return compType.unDynamic() + return expType.unDynamic() } override fun toString(): String { @@ -42,21 +47,19 @@ open class ASTVariableNode : ASTTypedNode { fun toDefineC(): String { if (isLocal && !Compiler.isDefined(data)) Compiler.variables[Compiler.variables.size-1] += mapOf(data to DefinedType(expType)) - - if (getType().typeString.contains("array")){ - val s = getType().typeString.split("-") - return "${s[1]} $data[${s[2]}]" - } - if (getType().typeString.contains("pointer")){ + if (getType().isArray()){ val s = getType().typeString.split("-") - return "${s[1]} *$data" + val starcount = s.size - 3 + + val star = "*".repeat(starcount) + + return "${s[s.size-2]} $star$data[${s[s.size-1]}]" } - if (getType().isHeap){ - val s = getType().typeString.split("-") - return "${s[1]} *$data" + if (getType().isAddress()){ + val s = getType().getPrimitive() + return "${s} *$data" } - - return "${getType().typeString} $data" + return "${getType().toC()} $data" } - override fun toC(): String = if (Compiler.isDefined(data)) data else toDefineC() + override fun toC(sideEffect:Boolean): String = if (Compiler.isDefined(data) || !sideEffect) data else toDefineC() } \ No newline at end of file diff --git a/src/main/kotlin/Main.kt b/src/main/kotlin/Main.kt index 6edaa57..6ad0e7c 100644 --- a/src/main/kotlin/Main.kt +++ b/src/main/kotlin/Main.kt @@ -8,6 +8,8 @@ import cz.j_jzk.klang.prales.useful.list import czechtina.* import czechtina.header.createCzechtinaDefineFile import czechtina.lesana.czechtinaLesana +import utils.ArgsProvider +import utils.Filer import utils.RemoveFileExtension import java.io.BufferedReader import java.io.File @@ -24,30 +26,43 @@ fun main(args: Array) { | --fpeterek Uses macros from old czechtina.h file | --friendly Generate valid C without macros in comment bellow code | --set-dir Set dir for file creation + | --debug Show debug info """.trimMargin()) - if (args.any(){it == "--fpeterek"}) { + ArgsProvider.processArgs(args) + + if (ArgsProvider.fpeterek) { Compiler.setToCZ() } - val setDirIndex = args.indexOf("--set-dir") - if (setDirIndex != -1 && setDirIndex != args.size -1) { - Compiler.buildPath= args[setDirIndex+1] + "/" + if (ArgsProvider.setDir) { + Compiler.buildPath= ArgsProvider.dir } //create file with name of input file in current directory - val file = args.firstOrNull() ?: return println("No input file specified") + val file = args.firstOrNull() ?: return println("[FAT]: no input file") + + var text = "" try { - Compiler.compileFile(file, args) + text= Filer.readFromFile(file) } catch (e: Exception) { - println("Error: variables scope ${Compiler.variables}") - println("Error: ${e.message}") - throw e + return println("[FAT]: file can't be read") + } + + try { + Compiler.compile(text,file) + } + catch (e: Exception) { + println("[ERR]: ${e.message}") + if (ArgsProvider.debug) + e.printStackTrace() + return + } - if (args.any() { it == "--no-compile" }) { + if (ArgsProvider.noCompilation) { return } @@ -67,8 +82,6 @@ fun main(args: Array) { println(line) } - val exitCode = process.waitFor() - println("Exited with error code $exitCode") } catch (e: Exception) { e.printStackTrace() } diff --git a/src/main/kotlin/compiler/Compiler.kt b/src/main/kotlin/compiler/Compiler.kt index 6ff2105..95e37bc 100644 --- a/src/main/kotlin/compiler/Compiler.kt +++ b/src/main/kotlin/compiler/Compiler.kt @@ -2,28 +2,23 @@ package compiler import AST.* import cz.j_jzk.klang.input.InputFactory -import czechtina.* +import czechtina.grammar.* import czechtina.header.createCzechtinaDefineFile import czechtina.lesana.czechtinaLesana +import utils.ArgsProvider import java.io.File +import kotlin.system.exitProcess object Compiler { - val VERSION = "0.1.5" + private const val VERSION = "0.1.6" + private const val red = "\u001b[31m" + private const val reset = "\u001b[0m" var isParsed = false - var compilingTo = "C" - var definedTypes = mutableListOf() - var definedFunctions = mutableMapOf( - "printf" to DefinedFunction("printf",DefinedType("void"), listOf(DefinedFunctionVariant("printf", listOf(DefinedType("string")))), virtual = true), - "new" to DefinedFunction("new",DefinedType("pointer-void", true), listOf(DefinedFunctionVariant("malloc", listOf( - DefinedType("int") - ))), virtual = true), - "predej" to DefinedFunction("predej",DefinedType("pointer-void"), listOf(DefinedFunctionVariant("", listOf( - DefinedType("pointer", isHeap = true) - ))), virtual = true), - "const" to DefinedFunction("const",DefinedType("pointer-void"), listOf(DefinedFunctionVariant("", listOf( - DefinedType("pointer") - ))), virtual = true), - ) + private var compilingTo = "C" + var definedTypes = mutableMapOf() + var undefinedFunction = mutableListOf() + var definedFunctions = initDefinedFunction() + var definedStructures = mutableMapOf() var variables = mutableListOf(mutableMapOf()) var grammar: Map = C var buildPath:String = "" @@ -34,15 +29,24 @@ object Compiler { else -> "" } - fun setToC() { + private fun setToC() { compilingTo = "C" grammar = C } - fun addToDefinedTypes(type: String) = definedTypes.add(type) + fun addToDefinedTypes(type: String, definedType: DefinedType): Boolean { + definedTypes += mapOf(type to definedType) + return true + } + + fun tryGetDefinedType(type: String) : DefinedType? { + if (definedTypes.containsKey(type)) + return definedTypes[type]!! + return null + } fun controlDefinedVariables(varName: String): Boolean { - if (definedTypes.any { it == varName }) + if (definedTypes.keys.any { it == varName }) throw Exception("Variable $varName is defined as type") if (definedFunctions.containsKey(varName)) throw Exception("Variable $varName is defined as function") @@ -51,6 +55,23 @@ object Compiler { return true } + fun setNewVariableType(varName: String, type: DefinedType) { + if (variables[variables.size - 1].containsKey(varName)) { + setVariableType(varName, type) + return + } + variables[variables.size - 1] += mapOf(varName to type) + } + + fun setVariableType(varName: String, type: DefinedType) { + for (variable in variables.reversed()){ + if (variable.containsKey(varName)) { + variable[varName] = type + return + } + } + } + fun getVariableType(varName: String): DefinedType? { for (variable in variables.reversed()) if (variable.containsKey(varName)) @@ -78,8 +99,8 @@ object Compiler { if (variables.size == 1) throw Exception("Cant pop global scope") variables[variables.size -1].forEach { - if (it.value.isHeap && !it.value.dealocated) - retVal += "free(${it.key});\n\t" + if (it.value.isDynamic() && !it.value.dealocated) + retVal += "if(${it.key})free(${it.key});\n\t" } variables.removeAt(variables.size - 1) if (retVal != init) @@ -97,8 +118,6 @@ object Compiler { private fun calcTypePriority(type: String) : Int = when (type) { "none" -> 100 - "dynamic" -> 15 - "pointer" -> 10 "string" -> 5 "double" -> 4 "float" -> 3 @@ -106,21 +125,39 @@ object Compiler { "char" -> 1 "bool" -> 1 "void" -> 0 - else -> if (type.contains("array")) 0 else throw Exception("Unhandled Type $type") + else -> if (type.contains("-")) 0 else throw Exception("Unhandled Type $type") } - fun calcBinaryType(left: ASTTypedNode, right: ASTTypedNode, operand: String): DefinedType { + fun calcBinaryType(left: ASTNode, right: ASTNode, operand: String): DefinedType { if (left.getType().isTemplate()) return DefinedType(left.getType()) if (right.getType().isTemplate()) return DefinedType(right.getType()) if (operand == "=") { if (left.getType().isConst && isParsed) - throw Exception("Cannot change const type of variable ${left}") + throw Exception("Cannot change const type of variable $left") if (left is ASTVariableNode) left.addType(right.getType()) return DefinedType(right.getType()) } + if (Regex(cAndCzechtinaRegex(AllComparation)).matches(operand)) + return DefinedType("bool") + + if (Regex(cAndCzechtinaRegex(listOf( GrammarToken.OPERATOR_MINUS))).matches(operand)) { + if (left.getType().isAddress() && right.getType().isAddress()) + return DefinedType("int") + if (left.getType().isAddress() && right.getType().getPrimitive().contains("int")) + return DefinedType(left.getType()) + if (left.getType().getPrimitive().contains("int") && right.getType().isAddress()) + return DefinedType(right.getType()) + } + if (Regex(cAndCzechtinaRegex(listOf( GrammarToken.OPERATOR_PLUS))).matches(operand)) { + if (left.getType().isAddress() && right.getType().getPrimitive().contains("int")){ + return DefinedType(left.getType()) + } + if (left.getType().getPrimitive().contains("int") && right.getType().isAddress()) + return DefinedType(right.getType()) + } try { val leftWeight = calcTypePriority(left.getType().typeString) @@ -134,14 +171,15 @@ object Compiler { if (operand == "%" && listOf(leftWeight, rightWeight).any { it == 3 || it == 4 }) throw Exception("Modulo cant be made with floating point number") - if (Regex(cAndCzechtinaRegex(AllComparation)).matches(operand)) - return DefinedType("bool"); if (leftWeight > rightWeight) return DefinedType(left.getType()) return DefinedType(right.getType()) } catch (e: Exception) { + println(operand) + println(left.getType()) + println(right.getType()) throw Exception("Error in calcBinaryType: ${e.message}") } } @@ -150,34 +188,8 @@ object Compiler { return "Compiler(compilingTo='$compilingTo', definedTypes=$definedTypes, definedFunctions=$definedFunctions, variables=$variables,)" } - fun isAnyUsedFunctionUndefined(): Boolean { - for (function in definedFunctions) - if (function.value.variants.any { !it.defined && it.timeUsed > 0 }) - return true - return false - } - - fun compileFile(path: String, args: Array) { - var code = Preprocessor.preprocess(path) - var withoutExtension = path.substring(0, path.length - 3) - val czechtina = czechtinaLesana() - - isParsed = false - - val tree = czechtina.parse(InputFactory.fromString(code, "code")) as ASTProgramNode - - isParsed = true - - if (args.any() { it == "--show-tree" }) { - println(tree.toString()) - } - - variables.clear() - variables.add(mutableMapOf()) - - var cCode = tree.toC() - - + private fun addFunctionVariants(code:String, tree: ASTProgramNode) : String { + var cCode = code while (isAnyUsedFunctionUndefined()) { for (function in definedFunctions){ val fce = function.value @@ -185,20 +197,19 @@ object Compiler { continue val variants = listOf(fce.variants).flatten().filter { !it.defined && it.timeUsed > 0 } for (variant in variants){ - val functionASTref = tree.functions.find { it.name == function.key } + val functionASTree = tree.functions.find { it.name == function.key } ?: throw Exception("Function ${function.key} not found") - Compiler.scopePush() - var functionAST = functionASTref.copy() - Compiler.scopePop() + scopePush() + val functionAST = functionASTree.copy() + scopePop() val paramsTypes = mutableListOf() for (param in functionAST.parameters) - if (param is ASTTypedNode) - paramsTypes.add(param.getType()) + paramsTypes.add(param.getType()) val abstractIndex = fce.validateParams(paramsTypes) - var retypeMap = mutableMapOf() + val retypeMap = mutableMapOf() for (i in 0 until variant.params.size){ - var old =fce.variants[abstractIndex].params[i] - var new = DefinedType(variant.params[i]) + val old =fce.variants[abstractIndex].params[i] + val new = DefinedType(variant.params[i]) if (old.isPointer()){ if (old.typeString.replace("pointer", "dynamic") == new.typeString) { @@ -232,15 +243,129 @@ object Compiler { } } } + return cCode + } + + + + private fun isAnyUsedFunctionUndefined(): Boolean { + for (function in definedFunctions) + if (function.value.variants.any { !it.defined && it.timeUsed > 0 }) + return true + return false + } + + val czechtina = czechtinaLesana() + fun compileText(text:String): String { + val preprocessed = Preprocessor.preprocessText(text, "") + isParsed = false + val tree: ASTProgramNode? + var cCode: String + try { + currentCode = preprocessed + tree = czechtina.parse(InputFactory.fromString(preprocessed, "code")) as ASTProgramNode + isParsed = true + for (function in tree.functions) + function.precalculateType() + variables.clear() + variables.add(mutableMapOf()) + cCode = tree.toC() + } catch (e:Exception) { + println(variables) + throw e + } + cCode = addFunctionVariants(cCode, tree) + cCode = cCode.replace("#\$#CZECHTINAMEZERA\$#\$", " ") + cCode = cCode.lines().filter { !it.contains("CZECHTINA ANCHOR") }.joinToString("\n") + return cCode + } + + + + var currentCode = "" + + var currentErrors = mutableMapOf>() + + fun getCurrentCodeLine(charIndex: Int) { + var line = 1 + var char = 0 + for (i in 0 until charIndex) { + if (currentCode[i] == '\n') { + line++ + char = 0 + } + else + char++ + } + + if (currentErrors.containsKey(line)) + currentErrors[line]!!.add(char) + else + currentErrors += mapOf(line to mutableListOf(char)) + } + + fun printCurrentErrors() { + for (error in currentErrors) { + println("$red[ERR]$reset: syntax error in line ${error.key} at char ${error.value.joinToString(",")}") + println(currentCode.split("\n")[error.key - 1]) + val max = error.value.max()!! + for (i in 0..max) + if (error.value.contains(i)) + print("$red^$reset") + else + print(" ") + println() + } + } + + fun compile(text: String, path: String) { + val code = Preprocessor.preprocessText(text, path) + val withoutExtension = path.substring(0, path.length - 3) + + isParsed = false + val tree: ASTProgramNode? + try { + currentCode = code + tree = czechtina.parse(InputFactory.fromString(code, "code")) as ASTProgramNode + } catch (e:Exception) { + printCurrentErrors() + println("$red[FATAL]: There is syntax error in your code$reset") + println("$red[FATAL]: ${e.message}$reset") + if (ArgsProvider.debug) + e.printStackTrace() + exitProcess(1) + } + + isParsed = true + + if (ArgsProvider.showTree) { + println(tree.toString()) + } + + variables.clear() + variables.add(mutableMapOf()) + + var cCode = "" + + try { + cCode = tree.toC() + } catch (e:Exception) { + println("$red[FATAL]: ${e.message}$reset") + if (ArgsProvider.debug) + e.printStackTrace() + exitProcess(1) + } + + cCode = addFunctionVariants(cCode, tree) - if (Compiler.compilingTo == "CZ") { + if (compilingTo == "CZ") { cCode = "#define \"czechtina.h\"\n$cCode" createCzechtinaDefineFile() } - if (args.any { it == "--friendly" }) { - if (Compiler.compilingTo != "C") { - Compiler.setToC() + if (ArgsProvider.friendly) { + if (compilingTo != "C") { + setToC() cCode += "\n/*\n ${tree.toC()} \n*/" } } @@ -249,10 +374,10 @@ object Compiler { cCode = cCode.lines().filter { !it.contains("CZECHTINA ANCHOR") }.joinToString("\n") - cCode= "// Czechtina ${Compiler.VERSION}\n$cCode" + cCode= "// Czechtina $VERSION\n$cCode" - if (args.any { it == "--write-code" }) { - cCode = "/*\n\t${Preprocessor.lastReadFile.replace("\n", "\n\t")}\n*/\n$cCode" + if (ArgsProvider.writeCode) { + cCode = "/*\n\t${text.replace("\n", "\n\t")}\n*/\n$cCode" } File("$buildPath$withoutExtension.c").writeText(cCode) } diff --git a/src/main/kotlin/compiler/DefinedFunction.kt b/src/main/kotlin/compiler/DefinedFunction.kt index 92b9e27..78879c6 100644 --- a/src/main/kotlin/compiler/DefinedFunction.kt +++ b/src/main/kotlin/compiler/DefinedFunction.kt @@ -57,7 +57,7 @@ class DefinedFunction(val name: String,val returnType: DefinedType, val virtual: return variants.size-1 } } - throw Exception("Function $name with params ${params.joinToString(",")} not found") + throw Exception("Function $name with params ${params.joinToString(","){it.typeString}} in [${variants}] not found") } override fun toString(): String = "\n$name: $returnType $virtual (\n\t${variants.joinToString("\n\t")}\n)" diff --git a/src/main/kotlin/compiler/DefinedStructure.kt b/src/main/kotlin/compiler/DefinedStructure.kt new file mode 100644 index 0000000..f72e4cf --- /dev/null +++ b/src/main/kotlin/compiler/DefinedStructure.kt @@ -0,0 +1,13 @@ +package compiler + + +class DefinedStructure(val name:String, val type: DefinedType, var properties: Map) { + + fun getPropType(name:String): DefinedType = properties[name] ?: throw Exception("${this.name} doesnt have type $name") + + fun addPropType(name: String, type: DefinedType) { + if (properties.containsKey(name)) + throw Exception("${this.name} already have type $name") + properties += mapOf(name to type) + } +} \ No newline at end of file diff --git a/src/main/kotlin/compiler/DefinedType.kt b/src/main/kotlin/compiler/DefinedType.kt index 7658516..9d48e96 100644 --- a/src/main/kotlin/compiler/DefinedType.kt +++ b/src/main/kotlin/compiler/DefinedType.kt @@ -1,21 +1,24 @@ package compiler class DefinedType { - val typeString: String + var typeString: String val isHeap:Boolean val isConst:Boolean + val isStructured:Boolean var dealocated: Boolean = false - constructor(typeString: String, isHeap: Boolean = false, isConst: Boolean = false) { + constructor(typeString: String, isHeap: Boolean = false, isConst: Boolean = false, isStructured: Boolean = false) { this.typeString = typeString this.isHeap = isHeap this.isConst = isConst + this.isStructured = isStructured } constructor(DT: DefinedType) { this.typeString = DT.typeString this.isHeap = DT.isHeap this.isConst = DT.isConst + this.isStructured = DT.isStructured } fun toC(): String { @@ -24,35 +27,44 @@ class DefinedType { return getPrimitive() } - fun toHeap(): DefinedType { - return DefinedType(typeString, true) + fun toArray(size: String): DefinedType { + if (isPointer() || isDynamic()) + return changeTypeString("array-${this.typeString}-${size}") + return changeTypeString("array-${this.typeString}-${size}") } - fun toConst(): DefinedType { - return DefinedType(typeString, isHeap, true) - } + fun toHeap(): DefinedType = DefinedType(typeString, true, isConst, isStructured) + fun toConst(): DefinedType = DefinedType(typeString, isHeap, true, isStructured) + fun toPointer(): DefinedType = changeTypeString("pointer-$typeString") - fun toPointer(): DefinedType { - return DefinedType("pointer-$typeString", isHeap) - } + fun toDereference(): DefinedType { + if (!isAddress() && Compiler.isParsed) + throw Exception("Cannot convert non-pointer type to dereference - $this") + if (isAddress()) + return changeTypeString(typeString.substring(typeString.indexOf("-")+1)) + return changeTypeString("none") + } fun toDynamic(): DefinedType { if (!isHeap) throw Exception("Cannot convert non-heap type to dynamic") - return DefinedType(typeString.replace("pointer","dynamic"), isHeap) + return changeTypeString(typeString.replace("pointer","dynamic")) } - fun isPointer(): Boolean { - return typeString.contains("pointer") - } - fun isDynamic(): Boolean { - return typeString.contains("dynamic") + fun changeTypeString(newTypeString: String):DefinedType { + var newT = DefinedType(this) + newT.typeString = newTypeString + return newT } - fun isTemplate(): Boolean { - return typeString.contains("*") - } + fun isAddress() = isPointer() || isDynamic() || isArray() + fun isPointer() = typeString.contains("pointer") + fun isDynamic() = typeString.contains("dynamic") + fun isArray() = typeString.contains("array") + + fun isTemplate(): Boolean = typeString.contains("*") + fun getTemplate(): String { if (!isTemplate()) throw Exception("Type $typeString is not template") @@ -70,5 +82,11 @@ class DefinedType { } - override fun toString(): String = "$typeString - $isHeap - $isConst" + override fun toString(): String = "$typeString ${if (isHeap) "Heap " else ""}${if (isConst) "Const " else ""}${if (isStructured) "Struct" else " "}" + + fun unDynamic(): DefinedType { + if (isDynamic()) + return changeTypeString(typeString.replace("dynamic","pointer")) + return DefinedType(this) + } } \ No newline at end of file diff --git a/src/main/kotlin/compiler/Preprocessor.kt b/src/main/kotlin/compiler/Preprocessor.kt index 1fd347d..a8b0907 100644 --- a/src/main/kotlin/compiler/Preprocessor.kt +++ b/src/main/kotlin/compiler/Preprocessor.kt @@ -1,32 +1,21 @@ package compiler +import compiler.Compiler.undefinedFunction import utils.GetFileLinkedFilePath import java.io.File object Preprocessor { - var lastReadFile: String = "" - fun readFile(filePath: String) { - lastReadFile = File(filePath).readText() + fun addUndefineFile(line: String):String { + undefinedFunction.add(line.split(" ")[1]) + return "" } - fun preprocess (filePath: String) :String { - readFile(filePath) - - val splitedCode = lastReadFile.split("\"").toMutableList() - - for (i in 0 until splitedCode.size) { - if (i % 2 == 0) continue - splitedCode[i] = splitedCode[i].replace("\\n", "\\\\n") - splitedCode[i] = splitedCode[i].replace("\\t", "\\t") - splitedCode[i] = splitedCode[i].replace(" ", "#\$#CZECHTINAMEZERA\$#\$") - } - var code = splitedCode.joinToString("\"").trim() - - val bylines = code.lines().toMutableList() + fun preprocessText (text:String, filePath: String): String { + val bylines = text.trim().lines().toTypedArray() var blocklevel = 0 - for (i in 0 until bylines.size) { + for (i in bylines.indices) { if (bylines[i].isBlank()) continue else if (bylines[i].contains("{")) @@ -37,16 +26,18 @@ object Preprocessor { continue else if (bylines[i].startsWith("pripoj c")) continue + else if (bylines[i].startsWith("#undefine")) + bylines[i] = addUndefineFile(bylines[i]) else if (bylines[i].startsWith("pripoj")) bylines[i] = File(GetFileLinkedFilePath(filePath, bylines[i].split(" ")[1])).readText() else if (!bylines[i].endsWith("\\") && blocklevel >0) bylines[i] = "${bylines[i]};".replace(";;",";") else if (!bylines[i].endsWith("\\")) bylines[i] = "${bylines[i]} konec".replace(";;",";") + else if (bylines[i].endsWith("\\")) + bylines[i] = bylines[i].substringBeforeLast("\\") } - code = bylines.joinToString("\n") - - return code; + return bylines.joinToString("\n") } } \ No newline at end of file diff --git a/src/main/kotlin/compiler/initialization.kt b/src/main/kotlin/compiler/initialization.kt new file mode 100644 index 0000000..2417b4b --- /dev/null +++ b/src/main/kotlin/compiler/initialization.kt @@ -0,0 +1,49 @@ +package compiler + +fun initDefinedFunction() = mutableMapOf( + "printf" to DefinedFunction( + "printf", + DefinedType("void"), + listOf(DefinedFunctionVariant("printf", listOf(DefinedType("string")), enableArgs = true)), + virtual = true + ), + "scanf" to DefinedFunction( + "scanf", + DefinedType("scanf"), + listOf(DefinedFunctionVariant("scanf", listOf(DefinedType("string")), enableArgs = true)), + virtual = true + ), + "new" to DefinedFunction( + "new", DefinedType("dynamic-void", true), listOf( + DefinedFunctionVariant( + "malloc", listOf( + DefinedType("int") + ) + ) + ), virtual = true + ), + "predej" to DefinedFunction( + "predej", DefinedType("pointer-void"), listOf( + DefinedFunctionVariant( + "", listOf( + DefinedType("pointer", isHeap = true) + ) + ) + ), virtual = true + ), + "const" to DefinedFunction( + "const", DefinedType("pointer-void"), listOf( + DefinedFunctionVariant( + "", listOf( + DefinedType("pointer") + ) + ) + ), virtual = true + ), + "throw" to DefinedFunction( + "throw", + DefinedType("void"), + listOf(DefinedFunctionVariant("throw", listOf(DefinedType("string")), enableArgs = true)), + virtual = true + ), +) \ No newline at end of file diff --git a/src/main/kotlin/czechtina/grammar/grammar.kt b/src/main/kotlin/czechtina/grammar/grammar.kt new file mode 100644 index 0000000..88e7f4f --- /dev/null +++ b/src/main/kotlin/czechtina/grammar/grammar.kt @@ -0,0 +1,62 @@ +package czechtina.grammar + +val AllComparation = listOf( + GrammarToken.OPERATOR_EQUAL, + GrammarToken.OPERATOR_NOT_EQUAL, + GrammarToken.OPERATOR_LESS, + GrammarToken.OPERATOR_LESS_OR_EQUAL, + GrammarToken.OPERATOR_GREATER, + GrammarToken.OPERATOR_GREATER_OR_EQUAL, +) + +enum class GrammarToken { + TYPE_VOID, + TYPE_INTEGER, + TYPE_DECIMAL, + TYPE_BOOLEAN, + TYPE_CHAR, + TYPE_POINTER, + TYPE_ADDRESS, + TYPE_VALUE, + TYPE_ARRAY, + OPERATOR_PLUS, + OPERATOR_MINUS, + OPERATOR_MULTIPLY, + OPERATOR_DIVIDE, + OPERATOR_MODULO, + OPERATOR_ASSIGN, + OPERATOR_EQUAL, + OPERATOR_NOT_EQUAL, + OPERATOR_LESS, + OPERATOR_LESS_OR_EQUAL, + OPERATOR_GREATER, + OPERATOR_GREATER_OR_EQUAL, + OPERATOR_AND, + OPERATOR_OR, + OPERATOR_NOT, + OPERATOR_ITERATE, + KEYWORD_IF, + KEYWORD_ELSE, + KEYWORD_WHILE, + KEYWORD_FOR, + KEYWORD_FUNCTION_CALL, + KEYWORD_RETURN, + KEYWORD_BREAK, + KEYWORD_CONTINUE, + KEYWORD_VAR_DEFINITION, + KEYWORD_IMPORT, + KEYWORD_IMPORT_C, + KEYWORD_RANGE_DEFINITION, + KEYWORD_TYPE_DEFINITION, + KEYWORD_AS, + KEYWORD_STRUCT, + VARIABLE, +} + +val Alltypes = listOf( + GrammarToken.TYPE_VOID, + GrammarToken.TYPE_INTEGER, + GrammarToken.TYPE_DECIMAL, + GrammarToken.TYPE_BOOLEAN, + GrammarToken.TYPE_CHAR, +) \ No newline at end of file diff --git a/src/main/kotlin/czechtina/regex.kt b/src/main/kotlin/czechtina/grammar/mappers.kt similarity index 63% rename from src/main/kotlin/czechtina/regex.kt rename to src/main/kotlin/czechtina/grammar/mappers.kt index f977dcc..757fc12 100644 --- a/src/main/kotlin/czechtina/regex.kt +++ b/src/main/kotlin/czechtina/grammar/mappers.kt @@ -1,65 +1,4 @@ -package czechtina - -enum class GrammarToken { - TYPE_VOID, - TYPE_INTEGER, - TYPE_DECIMAL, - TYPE_BOOLEAN, - TYPE_CHAR, - TYPE_POINTER, - TYPE_ADDRESS, - TYPE_VALUE, - TYPE_ARRAY, - OPERATOR_PLUS, - OPERATOR_MINUS, - OPERATOR_MULTIPLY, - OPERATOR_DIVIDE, - OPERATOR_MODULO, - OPERATOR_ASSIGN, - OPERATOR_EQUAL, - OPERATOR_NOT_EQUAL, - OPERATOR_LESS, - OPERATOR_LESS_OR_EQUAL, - OPERATOR_GREATER, - OPERATOR_GREATER_OR_EQUAL, - OPERATOR_AND, - OPERATOR_OR, - OPERATOR_NOT, - OPERATOR_ITERATE, - KEYWORD_IF, - KEYWORD_ELSE, - KEYWORD_WHILE, - KEYWORD_FOR, - KEYWORD_FUNCTION_CALL, - KEYWORD_RETURN, - KEYWORD_BREAK, - KEYWORD_CONTINUE, - KEYWORD_VAR_DEFINITION, - KEYWORD_IMPORT, - KEYWORD_IMPORT_C, - KEYWORD_RANGE_DEFINITION, - KEYWORD_TYPE_DEFINITION, - KEYWORD_AS, - VARIABLE, -} - -val Alltypes = listOf( - GrammarToken.TYPE_VOID, - GrammarToken.TYPE_INTEGER, - GrammarToken.TYPE_DECIMAL, - GrammarToken.TYPE_BOOLEAN, - GrammarToken.TYPE_CHAR, -) - -val AllComparation = listOf( - GrammarToken.OPERATOR_EQUAL, - GrammarToken.OPERATOR_NOT_EQUAL, - GrammarToken.OPERATOR_LESS, - GrammarToken.OPERATOR_LESS_OR_EQUAL, - GrammarToken.OPERATOR_GREATER, - GrammarToken.OPERATOR_GREATER_OR_EQUAL, -) - +package czechtina.grammar val czechtina = mapOf( GrammarToken.TYPE_VOID to "void", @@ -101,9 +40,9 @@ val czechtina = mapOf( GrammarToken.KEYWORD_RANGE_DEFINITION to "do|az", GrammarToken.KEYWORD_TYPE_DEFINITION to "typ", GrammarToken.KEYWORD_AS to "jako|as", - GrammarToken.VARIABLE to "[a-zA-Z][a-zA-Z0-9]*", + GrammarToken.KEYWORD_STRUCT to "struct", + GrammarToken.VARIABLE to "[a-z][a-zA-Z0-9]*", ) - val C = mapOf( GrammarToken.TYPE_VOID to "void", GrammarToken.TYPE_INTEGER to "int", @@ -134,10 +73,8 @@ val C = mapOf( GrammarToken.KEYWORD_BREAK to "break", GrammarToken.KEYWORD_TYPE_DEFINITION to "typedef", GrammarToken.KEYWORD_CONTINUE to "continue", - GrammarToken.VARIABLE to "[a-zA-Z][a-zA-Z0-9]*", + GrammarToken.VARIABLE to "[a-z][a-zA-Z0-9]*", ) - - val CZ = mapOf( GrammarToken.TYPE_VOID to "void", GrammarToken.TYPE_INTEGER to "cele", @@ -169,39 +106,4 @@ val CZ = mapOf( GrammarToken.KEYWORD_TYPE_DEFINITION to "zadejtyp", GrammarToken.KEYWORD_CONTINUE to "pokracuj", GrammarToken.VARIABLE to "[a-zA-Z][a-zA-Z0-9]*", -) - -fun cTypeFromCzechtina (czechType: String): String { - for (type in GrammarToken.values()) { - if (type == GrammarToken.VARIABLE) - continue - var cValue = C.entries.find { it.key == type }?.value ?: "" - cValue = cValue.replace("\\", "") - if (Regex(czechtina[type]!!).matches(czechType)) - return cValue - - if (czechType == cValue) - return cValue - } - throw Exception("Unknown type $czechType") -} - - -fun czTypeFromCzechtina (czechType: String): String { - for (type in GrammarToken.values()) { - if (type == GrammarToken.VARIABLE) - continue - var cValue = CZ.entries.find { it.key == type }?.value ?: "" - cValue = cValue.replace("\\", "") - if (Regex(czechtina[type]!!).matches(czechType)) - return cValue - - if (czechType == cValue) - return cValue - } - throw Exception("Unknown type $czechType") -} - -fun cAndCzechtinaRegex (list: List): String = list.joinToString("|") { czechtina[it]!! + "|" + C[it]!! } - -fun czechtinaRegex (list: List): String = list.joinToString("|") { czechtina[it]!! } \ No newline at end of file +) \ No newline at end of file diff --git a/src/main/kotlin/czechtina/grammar/regex.kt b/src/main/kotlin/czechtina/grammar/regex.kt new file mode 100644 index 0000000..258b5b1 --- /dev/null +++ b/src/main/kotlin/czechtina/grammar/regex.kt @@ -0,0 +1,37 @@ +package czechtina.grammar + + +fun cTypeFromCzechtina (czechType: String): String { + for (type in GrammarToken.values()) { + if (type == GrammarToken.VARIABLE) + continue + var cValue = C.entries.find { it.key == type }?.value ?: "" + cValue = cValue.replace("\\", "") + if (Regex(czechtina[type]!!).matches(czechType)) + return cValue + + if (czechType == cValue) + return cValue + } + throw Exception("Unknown type $czechType") +} + + +fun czTypeFromCzechtina (czechType: String): String { + for (type in GrammarToken.values()) { + if (type == GrammarToken.VARIABLE) + continue + var cValue = CZ.entries.find { it.key == type }?.value ?: "" + cValue = cValue.replace("\\", "") + if (Regex(czechtina[type]!!).matches(czechType)) + return cValue + + if (czechType == cValue) + return cValue + } + throw Exception("Unknown type $czechType") +} + +fun cAndCzechtinaRegex (list: List): String = list.joinToString("|") { czechtina[it]!! + "|" + C[it]!! } + +fun czechtinaRegex (list: List): String = list.joinToString("|") { czechtina[it]!! } \ No newline at end of file diff --git a/src/main/kotlin/czechtina/header/czechheader.kt b/src/main/kotlin/czechtina/header/czechheader.kt index 3b4f7c7..85541b0 100644 --- a/src/main/kotlin/czechtina/header/czechheader.kt +++ b/src/main/kotlin/czechtina/header/czechheader.kt @@ -1,10 +1,10 @@ package czechtina.header import compiler.Compiler -import czechtina.C -import czechtina.CZ -import czechtina.GrammarToken -import czechtina.czechtina +import czechtina.grammar.C +import czechtina.grammar.CZ +import czechtina.grammar.GrammarToken +import czechtina.grammar.czechtina import java.io.File diff --git a/src/main/kotlin/czechtina/lesana/ProgramCode.kt b/src/main/kotlin/czechtina/lesana/ProgramCode.kt deleted file mode 100644 index 69fa525..0000000 --- a/src/main/kotlin/czechtina/lesana/ProgramCode.kt +++ /dev/null @@ -1,7 +0,0 @@ -package czechtina.lesana - -import AST.ASTNode -import cz.j_jzk.klang.lesana.lesana - - -//fun ProgramCodeLesana = lesana { } \ No newline at end of file diff --git a/src/main/kotlin/czechtina/lesana/ProgramLine.kt b/src/main/kotlin/czechtina/lesana/ProgramLine.kt new file mode 100644 index 0000000..b54979d --- /dev/null +++ b/src/main/kotlin/czechtina/lesana/ProgramLine.kt @@ -0,0 +1,73 @@ +package czechtina.lesana + +import AST.* +import cz.j_jzk.klang.lesana.LesanaBuilder +import cz.j_jzk.klang.parse.NodeID +import czechtina.grammar.GrammarToken +import czechtina.grammar.cAndCzechtinaRegex + + +fun LesanaBuilder.programLine( + line: NodeID, + variables: NodeID, + types: NodeID, + r_expression: NodeID, + blockCode: NodeID, + endOfLine: NodeID, + varDefinition: NodeID, + operands: NodeID, + programLines: NodeID +) { + // FOR LOOP + forLoops(line, variables, types, r_expression, blockCode, endOfLine) + + + + line to def( + varDefinition, + endOfLine + ) { (v, _) -> ASTUnaryNode(ASTUnaryTypes.SEMICOLON, v) } + + line to def( + varDefinition, + operands, + r_expression, + endOfLine + ) { (v, o, l) -> ASTUnaryNode(ASTUnaryTypes.SEMICOLON, ASTOperandNode(o, v, l)) } + + line to def( + re(cAndCzechtinaRegex(listOf(GrammarToken.KEYWORD_RETURN))), + r_expression, + endOfLine + ) { ASTUnaryNode(ASTUnaryTypes.SEMICOLON, ASTUnaryNode(ASTUnaryTypes.RETURN, it.v2)) } + + line to def( + re(cAndCzechtinaRegex(listOf(GrammarToken.KEYWORD_RETURN))), + endOfLine + ) { + ASTUnaryNode( + ASTUnaryTypes.SEMICOLON, + ASTUnaryNode(ASTUnaryTypes.RETURN, ASTUnaryNode(ASTUnaryTypes.LITERAL, "")) + ) + } + + + flowControl(line, r_expression, blockCode) + + + + line to def( + r_expression, + endOfLine + ) { (l) -> ASTUnaryNode(ASTUnaryTypes.SEMICOLON, l) } + + + + + blockCode to def(re("{"), programLines, re("}")) { + ASTUnaryNode(ASTUnaryTypes.CURLY, it.v2) + } + + programLines to def(line, programLines) { ASTProgramLines(listOf(it.v1) + it.v2.programLines) } + programLines to def(line) { ASTProgramLines(listOf(it.v1)) } +} diff --git a/src/main/kotlin/czechtina/lesana/czechtinaLesana.kt b/src/main/kotlin/czechtina/lesana/czechtinaLesana.kt index e228293..f0658fc 100644 --- a/src/main/kotlin/czechtina/lesana/czechtinaLesana.kt +++ b/src/main/kotlin/czechtina/lesana/czechtinaLesana.kt @@ -3,10 +3,9 @@ package czechtina.lesana import AST.* import compiler.Compiler import compiler.DefinedType -import cz.j_jzk.klang.lesana.LesanaBuilder import cz.j_jzk.klang.lesana.lesana import cz.j_jzk.klang.parse.NodeID -import czechtina.* +import czechtina.grammar.* fun czechtinaLesana() = lesana { @@ -21,12 +20,31 @@ fun czechtinaLesana() = lesana { val programLines = NodeID("programLines") val line = NodeID("line") val typeDefinition = NodeID("typeDefinition") - val varDefinition = NodeID("varDefinition") + val varDefinition = NodeID("varDefinition") val listableDefinition = include(listAble(listOf(varDefinition))) val import = NodeID("import") - val r_expression = include(expression(variables, types)) val program = NodeID("program") + val structHead = NodeID("structure header") + val structure = NodeID("structure") + variableDefinition(varDefinition, variables, types) + + structHead to def( + re(czechtina[GrammarToken.KEYWORD_STRUCT]!!), + types, + re("{") + ) {ASTStructureNode(it.v2.data.toString(), emptyList()).defineItSelf()} + + structHead to def ( + structHead, + varDefinition, + endOfLine + ) {it.v1.addProperty(it.v2 as ASTVarDefinitionNode)} + + structure to def ( + structHead, + re("}") + ) {it.v1} typeDefinition to def( re(cAndCzechtinaRegex(listOf(GrammarToken.KEYWORD_TYPE_DEFINITION))), @@ -37,7 +55,7 @@ fun czechtinaLesana() = lesana { ) { (_, v,_, t,_) -> if (Compiler.definedTypes.contains(v.toC())) throw Exception("Type ${v.toC()} is already defined") - else if (Compiler.addToDefinedTypes(v.toC())) + else if (Compiler.addToDefinedTypes(v.toC(),t.getType())) ASTBinaryNode(ASTBinaryTypes.TYPE_DEFINITION, t, v.addType(t.getType())) else throw Exception("Error") @@ -53,55 +71,14 @@ fun czechtinaLesana() = lesana { re(">")) { (_, _, t2, _) -> ASTUnaryNode(ASTUnaryTypes.TYPE_POINTER, t2, t2.getType().toPointer()) } - types to def(re(cAndCzechtinaRegex(Alltypes)+"|T[0-9]*")) { ASTUnaryNode(ASTUnaryTypes.TYPE, if (it.v1.contains("T")) "*${it.v1}" else it.v1, DefinedType(if (it.v1.contains("T")) "*${it.v1}" else cTypeFromCzechtina(it.v1) )) } + types to def(re(cAndCzechtinaRegex(Alltypes) +"|T[0-9]*|[A-Z][a-zA-Z]*")) { ASTUnaryNode(ASTUnaryTypes.TYPE, if (Regex("T[0-9]*").matches(it.v1)) "*${it.v1}" else it.v1, DefinedType(if (Regex("T[0-9]*").matches(it.v1)) "*${it.v1}" else if (Regex("[A-Z]+").matches(it.v1)) it.v1 else cTypeFromCzechtina(it.v1) )) } operands to def(re(cAndCzechtinaRegex(listOf(GrammarToken.OPERATOR_ASSIGN)))) { it.v1 } - // FOR LOOP - forLoops(line, variables, types, r_expression, blockCode, endOfLine) - - - variableDefinition(varDefinition, variables, types) - - line to def( - varDefinition, - endOfLine - ) { (v, _) -> ASTUnaryNode(ASTUnaryTypes.SEMICOLON, v) } - - line to def( - varDefinition, - operands, - r_expression, - endOfLine - ) { (v, o, l) -> ASTUnaryNode(ASTUnaryTypes.SEMICOLON, ASTOperandNode(o, v, l)) } - - line to def( - re(cAndCzechtinaRegex(listOf(GrammarToken.KEYWORD_RETURN))), - r_expression, - endOfLine - ) { ASTUnaryNode(ASTUnaryTypes.SEMICOLON, ASTUnaryNode(ASTUnaryTypes.RETURN, it.v2)) } - - line to def( - re(cAndCzechtinaRegex(listOf(GrammarToken.KEYWORD_RETURN))), - endOfLine - ) { ASTUnaryNode(ASTUnaryTypes.SEMICOLON, ASTUnaryNode(ASTUnaryTypes.RETURN, ASTUnaryNode(ASTUnaryTypes.LITERAL, ""))) } - - - flowControl(line, r_expression, blockCode) - - - - line to def( - r_expression, - endOfLine - ) { (l) -> ASTUnaryNode(ASTUnaryTypes.SEMICOLON, l) } - - + val r_expression = include(expression(variables, types)) - blockCode to def(re("{"), programLines, re("}")) { - ASTUnaryNode(ASTUnaryTypes.CURLY, it.v2) - } + programLine(line, variables, types, r_expression, blockCode, endOfLine, varDefinition, operands, programLines) // MAIN FUNCTION main to def( @@ -109,7 +86,7 @@ fun czechtinaLesana() = lesana { ) { ASTFunctionNode( - ASTUnaryNode(ASTUnaryTypes.TYPE, "cele"), it.v1, + ASTUnaryNode(ASTUnaryTypes.TYPE, "cele", DefinedType("int")), it.v1, emptyList(), it.v2 ) } @@ -122,9 +99,6 @@ fun czechtinaLesana() = lesana { - programLines to def(line, programLines) { ASTProgramLines(listOf(it.v1) + it.v2.programLines) } - programLines to def(line) { ASTProgramLines(listOf(it.v1)) } - import to def(re(czechtinaRegex(listOf(GrammarToken.KEYWORD_IMPORT_C))), re("[a-zA-Z][a-zA-Z0-9]*")) { ASTUnaryNode( ASTUnaryTypes.IMPORT_C, @@ -141,14 +115,15 @@ fun czechtinaLesana() = lesana { program to def(program, tFunction) { (program, func) -> program.appendFunction(func) } program to def(main) { ASTProgramNode(listOf(), listOf(), it.v1) } + program to def(program, structure) { (program, structure) -> program.appendStructure(structure) } + program to def(structure, program) { (structure, program) -> program.appendStructure(structure) } + line to def (blockCode) { it.v1 } setTopNode(program) ignoreRegexes("\\s") onUnexpectedToken { err -> - println(err.got.toString()) - println("excepted: " + err.expectedIDs) - println("------------------") - println(err) + Compiler.getCurrentCodeLine(err.got.position.character) + throw Exception( "CZECHTINA ERROR") } }.getLesana() diff --git a/src/main/kotlin/czechtina/lesana/expression.kt b/src/main/kotlin/czechtina/lesana/expression.kt index 708395e..32eba7d 100644 --- a/src/main/kotlin/czechtina/lesana/expression.kt +++ b/src/main/kotlin/czechtina/lesana/expression.kt @@ -1,59 +1,66 @@ package czechtina.lesana import AST.* +import compiler.Compiler import compiler.DefinedType import cz.j_jzk.klang.lesana.lesana import cz.j_jzk.klang.parse.NodeID -import czechtina.AllComparation -import czechtina.GrammarToken -import czechtina.cAndCzechtinaRegex -import czechtina.czechtina +import czechtina.grammar.AllComparation +import czechtina.grammar.GrammarToken +import czechtina.grammar.cAndCzechtinaRegex +import czechtina.grammar.czechtina -fun expression(variables: NodeID, types: NodeID) = lesana { +fun expression(variables: NodeID, types: NodeID) = lesana { val literals = include(literals()) - val exp1 = NodeID("expressions") - val exp2 = NodeID("expressions") - val exp3 = NodeID("expressions") - val functionCalling = NodeID("functionCalling") - var para1 = NodeID("paragraph") - var para2 = NodeID("paragraph") - var para3 = NodeID("paragraph") - var para4 = NodeID("paragraph") - var para5 = NodeID("paragraph") - val sentence = NodeID("sentence") - val listexp3 = include(listAble(listOf(exp3, variables))) - - - variables to def(re("@"), variables) { (_, e) -> ASTFunctionCallNode( ASTVariableNode("const", DefinedType("none")), e) } - variables to def(re("&"), variables) { (_, e) -> ASTFunctionCallNode( ASTVariableNode("predej", DefinedType("none")), e) } - variables to def(variables, re("\\["), sentence, re("\\]")) { (v, _, e, _) -> ASTArrayAccessNode(v, e) } - variables to def(variables, re("\\["), variables, re("\\]")) { (v, _, e, _) -> ASTArrayAccessNode(v, e) } + val elevatedVariable = NodeID("elevatedVariable") + val exp1 = NodeID("expressions") + val exp2 = NodeID("expressions") + val exp3 = NodeID("expressions") + val functionCalling = NodeID("functionCalling") + var para1 = NodeID("paragraph") + var para2 = NodeID("paragraph") + var para3 = NodeID("paragraph") + var para4 = NodeID("paragraph") + var para5 = NodeID("paragraph") + val sentence = NodeID("sentence") + val listexp3 = include(listAble(listOf(exp3, elevatedVariable))) + + + elevatedVariable to def(re("@"), variables) { (_, e) -> ASTFunctionCallNode( ASTVariableNode("const", DefinedType("none")), e) } + elevatedVariable to def(re("&"), variables) { (_, e) -> ASTFunctionCallNode( ASTVariableNode("predej", DefinedType("none")), e) } + elevatedVariable to def(elevatedVariable, re("\\["), sentence, re("\\]")) { (v, _, e, _) -> ASTArrayAccessNode(v, e) } + elevatedVariable to def(elevatedVariable, re("\\["), elevatedVariable, re("\\]")) { (v, _, e, _) -> ASTArrayAccessNode(v, e) } + + + elevatedVariable to def (variables, re("\\."), variables) {ASTStructureAccessNode(it.v1, it.v3)} + elevatedVariable to def (elevatedVariable, re("\\."), variables) {ASTStructureAccessNode(it.v1, it.v3)} + exp1 to def(literals) { it.v1 } exp2 to def(exp2, re(cAndCzechtinaRegex(listOf(GrammarToken.OPERATOR_MULTIPLY, GrammarToken.OPERATOR_DIVIDE, GrammarToken.OPERATOR_MODULO))), exp1) { (e1, o, e2) -> ASTOperandNode(o, e1, e2) } - exp2 to def(variables, re(cAndCzechtinaRegex(listOf(GrammarToken.OPERATOR_MULTIPLY, GrammarToken.OPERATOR_DIVIDE, GrammarToken.OPERATOR_MODULO))), exp2) { (e1, o, e2) -> ASTOperandNode(o, e1, e2) } - exp2 to def(exp2, re(cAndCzechtinaRegex(listOf(GrammarToken.OPERATOR_MULTIPLY, GrammarToken.OPERATOR_DIVIDE, GrammarToken.OPERATOR_MODULO))), variables) { (e1, o, e2) -> ASTOperandNode(o, e1, e2) } - exp2 to def(variables, re(cAndCzechtinaRegex(listOf(GrammarToken.OPERATOR_MULTIPLY, GrammarToken.OPERATOR_DIVIDE, GrammarToken.OPERATOR_MODULO))), variables) { (e1, o, e2) -> ASTOperandNode(o, e1, e2) } + exp2 to def(elevatedVariable, re(cAndCzechtinaRegex(listOf(GrammarToken.OPERATOR_MULTIPLY, GrammarToken.OPERATOR_DIVIDE, GrammarToken.OPERATOR_MODULO))), exp2) { (e1, o, e2) -> ASTOperandNode(o, e1, e2) } + exp2 to def(exp2, re(cAndCzechtinaRegex(listOf(GrammarToken.OPERATOR_MULTIPLY, GrammarToken.OPERATOR_DIVIDE, GrammarToken.OPERATOR_MODULO))), elevatedVariable) { (e1, o, e2) -> ASTOperandNode(o, e1, e2) } + exp2 to def(elevatedVariable, re(cAndCzechtinaRegex(listOf(GrammarToken.OPERATOR_MULTIPLY, GrammarToken.OPERATOR_DIVIDE, GrammarToken.OPERATOR_MODULO))), elevatedVariable) { (e1, o, e2) -> ASTOperandNode(o, e1, e2) } exp2 to def(exp1) { it.v1 } exp3 to def(exp3, re(cAndCzechtinaRegex(listOf(GrammarToken.OPERATOR_PLUS, GrammarToken.OPERATOR_MINUS))), exp2) { (e1, o, e2) -> ASTOperandNode(o, e1, e2) } - exp3 to def(variables, re(cAndCzechtinaRegex(listOf(GrammarToken.OPERATOR_PLUS, GrammarToken.OPERATOR_MINUS))), exp3) { (e1, o, e2) -> ASTOperandNode(o, e1, e2) } - exp3 to def(exp3, re(cAndCzechtinaRegex(listOf(GrammarToken.OPERATOR_PLUS, GrammarToken.OPERATOR_MINUS))), variables) { (e1, o, e2) -> ASTOperandNode(o, e1, e2) } - exp3 to def(variables, re(cAndCzechtinaRegex(listOf(GrammarToken.OPERATOR_PLUS, GrammarToken.OPERATOR_MINUS))), variables) { (e1, o, e2) -> ASTOperandNode(o, e1, e2) } + exp3 to def(elevatedVariable, re(cAndCzechtinaRegex(listOf(GrammarToken.OPERATOR_PLUS, GrammarToken.OPERATOR_MINUS))), exp3) { (e1, o, e2) -> ASTOperandNode(o, e1, e2) } + exp3 to def(exp3, re(cAndCzechtinaRegex(listOf(GrammarToken.OPERATOR_PLUS, GrammarToken.OPERATOR_MINUS))), elevatedVariable) { (e1, o, e2) -> ASTOperandNode(o, e1, e2) } + exp3 to def(elevatedVariable, re(cAndCzechtinaRegex(listOf(GrammarToken.OPERATOR_PLUS, GrammarToken.OPERATOR_MINUS))), elevatedVariable) { (e1, o, e2) -> ASTOperandNode(o, e1, e2) } exp3 to def(exp2) { it.v1 } exp1 to def (re("\\(") , para5, re("\\)")) { ASTUnaryNode(ASTUnaryTypes.BRACKET, it.v2, it.v2.getType()) } exp1 to def (re("\\(") , functionCalling, re("\\)")) { ASTUnaryNode(ASTUnaryTypes.JUST_C, it.v2, it.v2.getType()) } - exp3 to def (re("\\["), listexp3, re("\\]")) { ASTUnaryNode(ASTUnaryTypes.ARRAY, it.v2, DefinedType("array-${it.v2.nodes[0].getType()}-${it.v2.nodes.size}")) } - functionCalling to def(re(czechtina[GrammarToken.KEYWORD_FUNCTION_CALL]!!), variables) { ASTFunctionCallNode(it.v2) } - functionCalling to def(variables, exp3) { (v, e) -> ASTFunctionCallNode( v, e) } - functionCalling to def(variables, functionCalling) { (v, e) -> ASTFunctionCallNode( v, e) } - functionCalling to def(variables, variables) { (v, e) -> ASTFunctionCallNode( v, e) } - functionCalling to def(variables, listexp3) { (v, e) -> ASTFunctionCallNode( v, e) } + functionCalling to def(re(czechtina[GrammarToken.KEYWORD_FUNCTION_CALL]!!), elevatedVariable) { ASTFunctionCallNode(it.v2) } + functionCalling to def(elevatedVariable, types) { (v, e) -> ASTFunctionCallNode( v, e) } + functionCalling to def(elevatedVariable, exp3) { (v, e) -> ASTFunctionCallNode( v, e) } + functionCalling to def(elevatedVariable, functionCalling) { (v, e) -> ASTFunctionCallNode( v, e) } + functionCalling to def(elevatedVariable, elevatedVariable) { (v, e) -> ASTFunctionCallNode( v, e) } + functionCalling to def(elevatedVariable, listexp3) { (v, e) -> ASTFunctionCallNode( v, e) } sentence to def(functionCalling) { it.v1 } @@ -63,29 +70,29 @@ fun expression(variables: NodeID, types: NodeID) { (e1, o, e2) -> if (e1 is ASTOperandNode && Regex(cAndCzechtinaRegex(AllComparation)).matches(e1.operand)) - ASTOperandNode(czechtina[GrammarToken.OPERATOR_AND]!!, e1, ASTOperandNode(o,e1.right!!, e2)) + ASTOperandNode(czechtina[GrammarToken.OPERATOR_AND]!!, e1, ASTOperandNode(o,e1.right, e2)) else if (e2 is ASTOperandNode && Regex(cAndCzechtinaRegex(AllComparation)).matches(e2.operand)) - ASTOperandNode(czechtina[GrammarToken.OPERATOR_AND]!!, ASTOperandNode(o,e1, e2.left!!),e2) + ASTOperandNode(czechtina[GrammarToken.OPERATOR_AND]!!, ASTOperandNode(o,e1, e2.left),e2) else ASTOperandNode(o, e1, e2) } - para1 to def(para1, re(cAndCzechtinaRegex(AllComparation)), variables ) + para1 to def(para1, re(cAndCzechtinaRegex(AllComparation)), elevatedVariable ) { (e1, o, e2) -> if (e1 is ASTOperandNode && Regex(cAndCzechtinaRegex(AllComparation)).matches(e1.operand)) - ASTOperandNode(czechtina[GrammarToken.OPERATOR_AND]!!, e1, ASTOperandNode(o,e1.right!!, e2)) + ASTOperandNode(czechtina[GrammarToken.OPERATOR_AND]!!, e1, ASTOperandNode(o,e1.right, e2)) else ASTOperandNode(o, e1, e2) } - para1 to def(variables, re(cAndCzechtinaRegex(AllComparation)), para1 ) + para1 to def(elevatedVariable, re(cAndCzechtinaRegex(AllComparation)), para1 ) { (e1, o, e2) -> if (e2 is ASTOperandNode && Regex(cAndCzechtinaRegex(AllComparation)).matches(e2.operand)) - ASTOperandNode(czechtina[GrammarToken.OPERATOR_AND]!!, ASTOperandNode(o,e1, e2.left!!),e2) + ASTOperandNode(czechtina[GrammarToken.OPERATOR_AND]!!, ASTOperandNode(o,e1, e2.left),e2) else ASTOperandNode(o, e1, e2) } - para1 to def(variables, re(cAndCzechtinaRegex(AllComparation)), variables ) + para1 to def(elevatedVariable, re(cAndCzechtinaRegex(AllComparation)), elevatedVariable ) { (e1, o, e2) -> ASTOperandNode(o, e1, e2) @@ -103,18 +110,18 @@ fun expression(variables: NodeID, types: NodeID) para4 to def(para4, re(czechtina[GrammarToken.KEYWORD_AS]!!), types) { ASTRetypeNode(it.v1, it.v3) } - para4 to def(variables, re(czechtina[GrammarToken.KEYWORD_AS]!!), types) { + para4 to def(elevatedVariable, re(czechtina[GrammarToken.KEYWORD_AS]!!), types) { ASTRetypeNode(it.v1, it.v3) } - para4 to def(variables, re(cAndCzechtinaRegex(listOf(GrammarToken.OPERATOR_ASSIGN))), para4) + para4 to def(elevatedVariable, re(cAndCzechtinaRegex(listOf(GrammarToken.OPERATOR_ASSIGN))), para4) { - ASTOperandNode(it.v2, it.v1.addType(it.v3.getType()), it.v3) + ASTOperandNode(it.v2, it.v1, it.v3) } - para4 to def(variables, re(cAndCzechtinaRegex(listOf(GrammarToken.OPERATOR_ASSIGN))), variables) + para4 to def(elevatedVariable, re(cAndCzechtinaRegex(listOf(GrammarToken.OPERATOR_ASSIGN))), elevatedVariable) { - ASTOperandNode(it.v2, it.v1.addType(it.v3.getType()), it.v3) + ASTOperandNode(it.v2, it.v1, it.v3) } para4 to def(para4, re(cAndCzechtinaRegex(listOf(GrammarToken.OPERATOR_ASSIGN))), para4) @@ -125,9 +132,18 @@ fun expression(variables: NodeID, types: NodeID) para4 to def(para3) {it.v1} para5 to def(para4) {it.v1} - para5 to def(variables) {it.v1} + para5 to def(elevatedVariable) {it.v1} + + elevatedVariable to def(variables) {it.v1} + exp3 to def (re("\\["), listexp3, re("\\]")) { ASTUnaryNode(ASTUnaryTypes.ARRAY, it.v2, DefinedType("array-${it.v2.nodes[0].getType().typeString}-${it.v2.nodes.size}")) } + sentence to def(re("-"), para5) { ASTUnaryNode(ASTUnaryTypes.MINUS, it.v2, it.v2.getType()) } inheritIgnoredREs() setTopNode(para5) + + onUnexpectedToken { err -> + Compiler.getCurrentCodeLine(err.got.position.character) + throw Exception("CZECHTINA EXPRESSION ERROR") + } } \ No newline at end of file diff --git a/src/main/kotlin/czechtina/lesana/flowControl.kt b/src/main/kotlin/czechtina/lesana/flowControl.kt index 2648d29..9df4613 100644 --- a/src/main/kotlin/czechtina/lesana/flowControl.kt +++ b/src/main/kotlin/czechtina/lesana/flowControl.kt @@ -3,15 +3,39 @@ package czechtina.lesana import AST.* import cz.j_jzk.klang.lesana.LesanaBuilder import cz.j_jzk.klang.parse.NodeID -import czechtina.GrammarToken -import czechtina.cAndCzechtinaRegex -import czechtina.czechtina +import czechtina.grammar.GrammarToken +import czechtina.grammar.cAndCzechtinaRegex +import czechtina.grammar.czechtina fun LesanaBuilder.flowControl( line: NodeID, - r_expression: NodeID, + r_expression: NodeID, blockCode: NodeID ) { + // while + + line to def( + re(cAndCzechtinaRegex(listOf(GrammarToken.KEYWORD_WHILE))), + r_expression, + blockCode + ) { (_, exp, block) -> ASTBinaryNode(ASTBinaryTypes.FLOW_CONTROL, ASTUnaryNode(ASTUnaryTypes.WHILE, exp), block) } + + line to def( + re(cAndCzechtinaRegex(listOf(GrammarToken.KEYWORD_WHILE))), + r_expression, + re(czechtina[GrammarToken.OPERATOR_ITERATE]!!), + line + ) { (_, exp, _, block) -> + ASTBinaryNode( + ASTBinaryTypes.FLOW_CONTROL, + ASTUnaryNode(ASTUnaryTypes.WHILE, exp), + ASTUnaryNode(ASTUnaryTypes.NEW_LINE, block) + ) + } + + + // if + line to def( re(cAndCzechtinaRegex(listOf(GrammarToken.KEYWORD_IF))), r_expression, diff --git a/src/main/kotlin/czechtina/lesana/forLoops.kt b/src/main/kotlin/czechtina/lesana/forLoops.kt index 210ad82..a42d11b 100644 --- a/src/main/kotlin/czechtina/lesana/forLoops.kt +++ b/src/main/kotlin/czechtina/lesana/forLoops.kt @@ -3,15 +3,15 @@ package czechtina.lesana import AST.* import cz.j_jzk.klang.lesana.LesanaBuilder import cz.j_jzk.klang.parse.NodeID -import czechtina.GrammarToken -import czechtina.cAndCzechtinaRegex -import czechtina.czechtina +import czechtina.grammar.GrammarToken +import czechtina.grammar.cAndCzechtinaRegex +import czechtina.grammar.czechtina fun LesanaBuilder.forLoops( line: NodeID, variables: NodeID, types: NodeID, - r_expression: NodeID, + r_expression: NodeID, blockCode: NodeID, endOfLine: NodeID ) { @@ -26,7 +26,7 @@ fun LesanaBuilder.forLoops( fun LesanaBuilder.rangedTypedFor( variables: NodeID, types: NodeID, - r_expression: NodeID, + r_expression: NodeID, blockCode: NodeID ): LesanaBuilder.IntermediateNodeDefinition = def( re(cAndCzechtinaRegex(listOf(GrammarToken.KEYWORD_FOR))), @@ -43,7 +43,7 @@ fun LesanaBuilder.rangedTypedFor( fun LesanaBuilder.inlineCodedFor( line: NodeID, - r_expression: NodeID, + r_expression: NodeID, endOfLine: NodeID ): LesanaBuilder.IntermediateNodeDefinition = def( re(cAndCzechtinaRegex(listOf(GrammarToken.KEYWORD_FOR))), @@ -57,7 +57,7 @@ fun LesanaBuilder.inlineCodedFor( fun LesanaBuilder.rangedFor( variables: NodeID, - r_expression: NodeID, + r_expression: NodeID, blockCode: NodeID ): LesanaBuilder.IntermediateNodeDefinition = def( re(cAndCzechtinaRegex(listOf(GrammarToken.KEYWORD_FOR))), @@ -81,7 +81,7 @@ fun LesanaBuilder.rangedFor( fun LesanaBuilder.codedFor( line: NodeID, - r_expression: NodeID, + r_expression: NodeID, endOfLine: NodeID, blockCode: NodeID ): LesanaBuilder.IntermediateNodeDefinition = def( @@ -95,7 +95,7 @@ fun LesanaBuilder.codedFor( fun LesanaBuilder.inlineRangedFor( variables: NodeID, - r_expression: NodeID, + r_expression: NodeID, line: NodeID ): LesanaBuilder.IntermediateNodeDefinition = def( re(cAndCzechtinaRegex(listOf(GrammarToken.KEYWORD_FOR))), @@ -121,7 +121,7 @@ fun LesanaBuilder.inlineRangedFor( fun LesanaBuilder.inlineTypedRangedFor( variables: NodeID, types: NodeID, - r_expression: NodeID, + r_expression: NodeID, line: NodeID ): LesanaBuilder.IntermediateNodeDefinition = def( re(cAndCzechtinaRegex(listOf(GrammarToken.KEYWORD_FOR))), diff --git a/src/main/kotlin/czechtina/lesana/function.kt b/src/main/kotlin/czechtina/lesana/function.kt index 0d64395..fe38b48 100644 --- a/src/main/kotlin/czechtina/lesana/function.kt +++ b/src/main/kotlin/czechtina/lesana/function.kt @@ -3,14 +3,14 @@ package czechtina.lesana import AST.* import cz.j_jzk.klang.lesana.LesanaBuilder import cz.j_jzk.klang.parse.NodeID -import czechtina.GrammarToken -import czechtina.czechtina +import czechtina.grammar.GrammarToken +import czechtina.grammar.czechtina fun LesanaBuilder.inlineFunction( tFunction: NodeID, - varDefinition: NodeID, - r_expression: NodeID, + varDefinition: NodeID, + r_expression: NodeID, konec: NodeID, listableDefinition: NodeID ) { @@ -23,7 +23,7 @@ fun LesanaBuilder.inlineFunction( ) { (funName, varDef, _, line) -> ASTFunctionNode( - ASTUnaryNode(ASTUnaryTypes.TYPE,data="", expressionType = line.getType()), + ASTUnaryNode(ASTUnaryTypes.TYPE,data= "", expressionType = line.getType()), funName, listOf(varDef), ASTUnaryNode( @@ -72,9 +72,9 @@ fun LesanaBuilder.inlineFunction( fun LesanaBuilder.inlineTypedFunction( tFunction: NodeID, - varDefinition: NodeID, + varDefinition: NodeID, types: NodeID, - r_expression: NodeID, + r_expression: NodeID, konec: NodeID, listableDefinition: NodeID ) { @@ -140,7 +140,7 @@ fun LesanaBuilder.inlineTypedFunction( fun LesanaBuilder.blockFunction( tFunction: NodeID, - varDefinition: NodeID, + varDefinition: NodeID, types: NodeID, programLines: NodeID, listableDefinition: NodeID diff --git a/src/main/kotlin/czechtina/lesana/listable.kt b/src/main/kotlin/czechtina/lesana/listable.kt index b1f3ab6..95dbf99 100644 --- a/src/main/kotlin/czechtina/lesana/listable.kt +++ b/src/main/kotlin/czechtina/lesana/listable.kt @@ -2,12 +2,12 @@ package czechtina.lesana import AST.ASTListNode import AST.ASTNode -import AST.ASTTypedNode +import compiler.Compiler import cz.j_jzk.klang.lesana.lesana import cz.j_jzk.klang.parse.NodeID -fun listAble (list: List>) = lesana { +fun listAble (list: List>) = lesana { val listAble = NodeID("listAble") for (i in list) { @@ -22,4 +22,9 @@ fun listAble (list: List>) = lesana { } setTopNode(listAble) inheritIgnoredREs() + + onUnexpectedToken { err -> + Compiler.getCurrentCodeLine(err.got.position.character) + throw Exception("CZECHTINA ListAble ERROR") + } } \ No newline at end of file diff --git a/src/main/kotlin/czechtina/lesana/literals.kt b/src/main/kotlin/czechtina/lesana/literals.kt index a4f72e9..56e3036 100644 --- a/src/main/kotlin/czechtina/lesana/literals.kt +++ b/src/main/kotlin/czechtina/lesana/literals.kt @@ -9,7 +9,6 @@ import cz.j_jzk.klang.prales.constants.decimal import cz.j_jzk.klang.prales.constants.integer import cz.j_jzk.klang.prales.constants.string - fun literals() = lesana { val literals = NodeID("literal") @@ -24,9 +23,7 @@ fun literals() = lesana { literals to def(include(boolean())) { ASTUnaryNode(ASTUnaryTypes.LITERAL, it.v1, DefinedType("bool")) } literals to def(re("'.'")) { ASTUnaryNode(ASTUnaryTypes.LITERAL, it.v1, DefinedType("char")) } literals to def(include(string())) { ASTUnaryNode(ASTUnaryTypes.STRING, it.v1, DefinedType("string")) } + inheritIgnoredREs() setTopNode(literals) } - - - diff --git a/src/main/kotlin/czechtina/lesana/variableDefinition.kt b/src/main/kotlin/czechtina/lesana/variableDefinition.kt index 1933147..9d1f41f 100644 --- a/src/main/kotlin/czechtina/lesana/variableDefinition.kt +++ b/src/main/kotlin/czechtina/lesana/variableDefinition.kt @@ -5,11 +5,11 @@ import compiler.Compiler import compiler.DefinedType import cz.j_jzk.klang.lesana.LesanaBuilder import cz.j_jzk.klang.parse.NodeID -import czechtina.GrammarToken -import czechtina.czechtina +import czechtina.grammar.GrammarToken +import czechtina.grammar.czechtina fun LesanaBuilder.variableDefinition( - varDefinition: NodeID, + varDefinition: NodeID, variables: NodeID, types: NodeID ) { @@ -20,7 +20,7 @@ fun LesanaBuilder.variableDefinition( re("<"), types, re(">") - ) { (v, _, _, _, t, _) -> ASTStaticArrayDefinitionNode(t, v.addType(DefinedType("array-${t.getType().typeString}-")), "") } + ) { (v, _, _, _, t, _) -> ASTStaticArrayDefinitionNode(t, v.addType(t.getType().toArray("")), "") } varDefinition to def( variables, @@ -31,7 +31,7 @@ fun LesanaBuilder.variableDefinition( re(","), re("[0-9]+"), re(">") - ) { (v, _, _, _, t, _, s, _) -> ASTStaticArrayDefinitionNode(t, v.addType(DefinedType("array-${t.getType().typeString}-$s")), s) } + ) { (v, _, _, _, t, _, s, _) -> ASTStaticArrayDefinitionNode(t, v.addType(t.getType().toArray(s)), s) } varDefinition to def( @@ -46,9 +46,9 @@ fun LesanaBuilder.variableDefinition( re(czechtina[GrammarToken.KEYWORD_VAR_DEFINITION]!!), variables ) { (v, _, t) -> - if (Compiler.definedTypes.contains(t.toC())) ASTVarDefinitionNode( - v, + if (Compiler.definedTypes.contains(t.data)) ASTVarDefinitionNode( + v.addType(Compiler.tryGetDefinedType(t.data) ?: throw Exception("ERROR")), t - ) else throw Exception("Variable ${t.toC()} is not defined as an type") + ) else throw Exception("[${Compiler.definedTypes}]:Variable ${t.toC()}/is not defined as an type") } } \ No newline at end of file diff --git a/src/main/kotlin/utils/ArgsProvider.kt b/src/main/kotlin/utils/ArgsProvider.kt new file mode 100644 index 0000000..be0cd7b --- /dev/null +++ b/src/main/kotlin/utils/ArgsProvider.kt @@ -0,0 +1,31 @@ +package utils + +object ArgsProvider { + var args: Array = arrayOf() + + var noCompilation: Boolean = false + var showTree: Boolean = false + var writeCode: Boolean = false + var fpeterek: Boolean = false + var friendly: Boolean = false + var setDir: Boolean = false + var debug: Boolean = false + var dir: String = "" + + fun processArgs(args: Array) { + this.args = args + + noCompilation = args.any { it == "--no-compile" } + showTree = args.any { it == "--show-tree" } + writeCode = args.any { it == "--write-code" } + fpeterek = args.any { it == "--fpeterek" } + friendly = args.any { it == "--friendly" } + setDir = args.any { it == "--set-dir" } + debug = args.any { it == "--debug" } + + val setDirIndex = args.indexOf("--set-dir") + if (setDirIndex != -1 && setDirIndex != args.size - 1) { + dir = args[setDirIndex + 1] + "/" + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/utils/Filer.kt b/src/main/kotlin/utils/Filer.kt new file mode 100644 index 0000000..4f6d05a --- /dev/null +++ b/src/main/kotlin/utils/Filer.kt @@ -0,0 +1,9 @@ +package utils + +import java.io.File + +object Filer { + fun readFromFile(path: String): String { + return File(path).readText() + } +} \ No newline at end of file diff --git a/src/test/kotlin/CodeBlockTest.kt b/src/test/kotlin/CodeBlockTest.kt new file mode 100644 index 0000000..5413b35 --- /dev/null +++ b/src/test/kotlin/CodeBlockTest.kt @@ -0,0 +1,221 @@ +import AST.ASTProgramNode +import compiler.Compiler +import cz.j_jzk.klang.input.InputFactory +import czechtina.lesana.czechtinaLesana +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test + +class CodeBlockTest { + + @Test + fun testHelloWorldCompilation() { + val code = """ + main { + printf "Hello world!" + } + """.trimIndent() + val cCode = Compiler.compileText(code) + val excepted = """int main() { + printf("Hello world!"); + }""".trimIndent() + assertEquals(excepted.replace("\\s+".toRegex(), " ").trim(), cCode.replace("\\s+".toRegex(), " ").trim()) + } + + @Test + fun testBasicNumericOperations() { + val code = """ + timesTwo x:int -> x * 2 + main { + x = 5 + 5 * 3 / 2 -1 + } + """.trimIndent() + val cCode = Compiler.compileText(code) + val excepted = """ + int timesTwo(int x); + + int timesTwo(int x) { + return x * 2; + } + + int main() { + int x = 5 + 5 * 3 / 2 - 1; + }""".trimIndent() + assertEquals(excepted.replace("\\s+".toRegex(), " ").trim(), cCode.replace("\\s+".toRegex(), " ").trim()) + } + + @Test + fun testInputOutputForStaticVariable() { + val code = """ + main { + x:int + scanf "%d", (adresa x) + printf "%d", x + } + """.trimIndent() + val cCode = Compiler.compileText(code) + val excepted = """ + int main() { + int x; + scanf("%d",&x); + printf("%d",x); + }""".trimIndent() + assertEquals(excepted.replace("\\s+".toRegex(), " ").trim(), cCode.replace("\\s+".toRegex(), " ").trim()) + + } + + @Test + fun testCreatingPointerToStaticVariable() { + val code = """ + main { + x:int + p = adresa x + h = hodnota p + } + """.trimIndent() + val cCode = Compiler.compileText(code) + val excepted = """ + int main() { + int x; + int *p = &x; + int h = *(p); + }""".trimIndent() + assertEquals(excepted.replace("\\s+".toRegex(), " ").trim(), cCode.replace("\\s+".toRegex(), " ").trim()) + } + + @Test + fun testCreatingHardCodedStaticArray() { + val code = """ + main { + x:pole = [1,2,3] + } + """.trimIndent() + val cCode = Compiler.compileText(code) + val excepted = """ + int main() { + int x[3] = {1,2,3}; + }""".trimIndent() + assertEquals(excepted.replace("\\s+".toRegex(), " ").trim(), cCode.replace("\\s+".toRegex(), " ").trim()) + } + + @Test + fun testCreatingDeducedStaticArray() { + val code = """ + main { + x = [1,2,3] + } + """.trimIndent() + val cCode = Compiler.compileText(code) + val excepted = """ + int main() { + int x[3] = {1,2,3}; + }""".trimIndent() + assertEquals(excepted.replace("\\s+".toRegex(), " ").trim(), cCode.replace("\\s+".toRegex(), " ").trim()) + } + + @Test + fun testPointerArithmetic() { + val code = """ + main { + x:pointer + y = x + 3 + z = x - y + } + """.trimIndent() + val cCode = Compiler.compileText(code) + val excepted = """ + int main() { + int *x; + int *y = x + 3; + int z = x - y; + }""".trimIndent() + assertEquals(excepted.replace("\\s+".toRegex(), " ").trim(), cCode.replace("\\s+".toRegex(), " ").trim()) + } + + @Test + fun testMorePointersArithmetic() { + val code = """ + main { + x = new 5 as pointer + z = x[3] + x[3] = 5 + } + """.trimIndent() + val cCode = Compiler.compileText(code) + val excepted = """ + int main() { char *x = (char*)(malloc(5)); char z = x[3]; x[3] = 5; if(x)free(x); }""".trimIndent() + assertEquals(excepted.replace("\\s+".toRegex(), " ").trim(), cCode.replace("\\s+".toRegex(), " ").trim()) + } + + + @Test + fun testHistogramProgram() { + val code = """ + pripoj c stdio + zpracuj histo:ukazatel,minimum:int { void + c:cele = 0 + scanf "%d", (adresa c) + for i:cele -> 0 do 9 { + if c == i+minimum{ + histo[i] = histo[i] + 1 + vrat + } + } + histo[9] = histo[9] + 1 + } + main { + t:znak + histo:pointer = [0,0,0,0,0,0,0,0,0,0] + scanf "%c", (adresa t) + if 'v' neni presne t neni presne 'h' { + printf "Neplatny mod vykresleni\\n" + vrat 1 + } + n:int + minimum:int + scanf "%d", (adresa n) + scanf "%d", (adresa minimum) + for i:cele -> 0 do n { + zpracuj histo, minimum + } + vrat 0 + } + """.trimIndent() + val cCode = Compiler.compileText(code) + val excepted = """ +#include "stdio.h" + +void zpracuj(int *histo, int minimum); + +void zpracuj(int *histo, int minimum) { + int c = 0; + scanf("%d",&c); + for (int i = 0; i < 9; i = i + 1) { + if (c == i + minimum) + histo[i] = histo[i] + 1; + return ; + } + } + histo[9] = histo[9] + 1; +} + +int main() { + char t; + int *histo = {0,0,0,0,0,0,0,0,0,0}; + scanf("%c",&t); + if ('v' != t && t != 'h') + printf("Neplatny mod vykresleni\n"); + return 1; + } + int n; + int minimum; + scanf("%d",&n); + scanf("%d",&minimum); + for (int i = 0; i < n; i = i + 1) { + zpracuj(histo,minimum); + } + return 0; +} + """.trimIndent() + assertEquals(excepted.replace("\\s+".toRegex(), " ").trim(), cCode.replace("\\s+".toRegex(), " ").trim()) + } +} \ No newline at end of file diff --git a/src/test/kotlin/StringTest.kt b/src/test/kotlin/StringTest.kt new file mode 100644 index 0000000..fbdde66 --- /dev/null +++ b/src/test/kotlin/StringTest.kt @@ -0,0 +1,39 @@ +import cz.j_jzk.klang.input.InputFactory +import czechtina.lesana.literals +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import cz.j_jzk.klang.lesana.lesana +import cz.j_jzk.klang.parse.NodeID +import AST.ASTUnaryNode + +class StringTest { + @Test + fun testWhitespaceRaw() { + val lsn = lesana { + val expr = NodeID("expr") + expr to def(include(literals())) { it.v1 } + setTopNode(expr) + + ignoreRegexes("\\s") + + onUnexpectedToken { println("$it; (expected one of: ${it.expectedIDs}") } + }.getLesana() + + fun checkString(source: String, expectedResult: String) = + assertEquals( + expectedResult, + (lsn.parse(InputFactory.fromString(source, "str")) as ASTUnaryNode).data as String + ) + + checkString("\"\"", "") + checkString("\"ahoj\"", "ahoj") + checkString("\"ahoj mami\"", "ahoj mami") + checkString("\"ahoj mami\n\"", "ahoj mami\n") + + } + + @Test + fun testWhitespaceInProgram() { + + } +} diff --git a/src/test/kotlin/StructureTest.kt b/src/test/kotlin/StructureTest.kt new file mode 100644 index 0000000..66835da --- /dev/null +++ b/src/test/kotlin/StructureTest.kt @@ -0,0 +1,133 @@ +import AST.ASTProgramNode +import compiler.Compiler +import cz.j_jzk.klang.input.InputFactory +import czechtina.lesana.czechtinaLesana +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test + +class StructureTest { + @Test + fun testStructures() { + val code = """ + struct DATA { + x:int + y:DATA + } + + main { + b = new DATA + b.x = 5 + a = b.x + } + """.trimIndent() + val cCode = Compiler.compileText(code) + val excepted = """ + typedef struct { int x; DATA *y; } DATA; + int main() { + DATA *b = (DATA *)malloc(sizeof(DATA)); + b->x = 5; + int a = b->x; + if(b)free(b); + } + """.trimIndent() + assertEquals(excepted.replace("\\s+".toRegex(), " ").trim(), cCode.replace("\\s+".toRegex(), " ").trim()) + } + + @Test + fun testStructArray() { + val code = """ + struct DATA { + data:int + next:DATA +} + +main { + a = new DATA + a.data = 1 + b = new DATA + b.data = 2 + a.next = b + arr:pole + arr[0] = a + arr[1] = b + printf "%d ", a.data + printf "%d ", a.next.data + printf "%d ", arr[0].data + printf "%d ", arr[0].next.data +} + """.trimIndent() + val cCode = Compiler.compileText(code) + val excepted = """ + typedef struct { + int data; + DATA *next; + } DATA; + +int main() { + DATA *a = (DATA *)malloc(sizeof(DATA)); + a->data = 1; + DATA *b = (DATA *)malloc(sizeof(DATA)); + b->data = 2; + a->next = b; + DATA *arr[3]; + arr[0] = a; + arr[1] = b; + printf("%d ",a->data); + printf("%d ",a->next->data); + printf("%d ",arr[0]->data); + printf("%d ",arr[0]->next->data); + if(a)free(a); + if(b)free(b); + +} + """.trimIndent() + assertEquals(excepted.replace("\\s+".toRegex(), " ").trim(), cCode.replace("\\s+".toRegex(), " ").trim()) + } + + @Test + fun testStructAsParameter() { + val code = """ +struct DATA { + data:int + next:DATA +} + +create b:DATA { void + b.data = 2 +} + +main { + a = new DATA + create a + create &a +} + """.trimIndent() + val cCode = Compiler.compileText(code) + val excepted = """ +typedef struct { + int data; + DATA *next; + } DATA; + +void create_v1(DATA *b); +void create(DATA *b); + +void create_v1(DATA *b) { + b->data = 2; + if(b)free(b); + +} +void create(DATA *b) { + b->data = 2; +} + +int main() { + DATA *a = (DATA *)malloc(sizeof(DATA)); + create(a); + create_v1(a); +} + """.trimIndent() + assertEquals(excepted.replace("\\s+".toRegex(), " ").trim(), cCode.replace("\\s+".toRegex(), " ").trim()) + } + +} \ No newline at end of file