Skip to content

Commit

Permalink
Add C generator
Browse files Browse the repository at this point in the history
  • Loading branch information
sake92 committed May 27, 2024
1 parent 9a36b96 commit e450a69
Show file tree
Hide file tree
Showing 11 changed files with 195 additions and 8 deletions.
2 changes: 1 addition & 1 deletion TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@

##


- disable zoom config


2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

inThisBuild(
List(
scalaVersion := "3.4.0",
scalaVersion := "3.4.2",
evictionErrorLevel := Level.Warn,
publish / skip := true,
scalafmtSbt := true,
Expand Down
3 changes: 2 additions & 1 deletion editor/src/main/scala/dev/sacode/flowrun/FlowRunEditor.scala
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ class FlowRunEditor(
// duplicate a <template>,
// move all its children to mountElem
// https://stackoverflow.com/a/20910214/4496364
private val template = dom.document.getElementById("flowrun-template").asDyn.content.cloneNode(true).asInstanceOf[dom.html.Element]
private val template =
dom.document.getElementById("flowrun-template").asDyn.content.cloneNode(true).asInstanceOf[dom.html.Element]
mountElem.innerText = ""
while template.childNodes.length > 0 do mountElem.appendChild(template.childNodes.head)

Expand Down
2 changes: 1 addition & 1 deletion editor/src/main/scala/dev/sacode/flowrun/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ object DomUtils {
dom.window.matchMedia("(pointer: coarse)").matches

def getNearestSvgNode(event: dom.MouseEvent): (String, dom.svg.G) = boundary {

getSvgNode(event.target).getOrElse {
for (i <- 1 to 48) { // search around mouse click for nearby edges
val nearNodes = List(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
package dev.sacode.flowrun.codegen

import scala.util.Try
import dev.sacode.flowrun.toIdentifier
import dev.sacode.flowrun.FlowRun
import dev.sacode.flowrun.ast.{Expression, *}
import Expression.Type
import dev.sacode.flowrun.eval.SymbolTable
import dev.sacode.flowrun.eval.SymbolKey
import dev.sacode.flowrun.eval.Symbol

class CGenerator(val programAst: Program) extends CodeGenerator {

def generate: Try[CodeGenRes] = Try {

addLine("#include <stdio.h>")
addLine("#include <stdbool.h>")

genMain()
programAst.functions.foreach(genFunction)

CodeGenRes(lines.toList, stmtLineNums.toMap)
}

private def genMain(): Unit = {
val function = programAst.main
symTab.enterScope(function.id, function.name)

addEmptyLine()
addLine(
"int main() {",
function.statements.head.id
)

incrIndent()
function.statements.foreach(genStatement)
addLine("return 0;")
decrIndent()

addLine("}", function.statements.head.id)

symTab.exitScope()
}

private def genFunction(function: Function): Unit = {
symTab.enterScope(function.id, function.name)

val params = function.parameters.map(p => s"${genType(p.tpe)} ${p.name}").mkString(", ")
addEmptyLine()
addLine(
s"${genType(function.tpe)} ${function.name}($params) {",
function.statements.head.id
)

incrIndent()
function.statements.foreach(genStatement)
decrIndent()

addLine("}", function.id)

symTab.exitScope()
}

private def genStatement(stmt: Statement): Unit = {
import Statement._
stmt match {
case _: Begin => // noop
case Declare(id, name, tpe, maybeInitValue) =>
val key = SymbolKey(name, Symbol.Kind.Variable, id)
symTab.add(id, key, tpe, None)
val initValue = maybeInitValue.getOrElse(defaultValue(tpe))
val initValueExpr = parseGenExpr(initValue)
tpe match
case Expression.Type.String =>
addLine(s"char ${name}[] = $initValueExpr;", id)
case _ => addLine(s"${genType(tpe)} $name = $initValueExpr;", id)

case Assign(id, name, value) =>
val genValue = parseGenExpr(value)
addLine(s"$name = $genValue;", id)

case Call(id, value) =>
val genValue = parseGenExpr(value)
addLine(s"$genValue;", id)

case Input(id, name, promptOpt) =>
val prompt = promptOpt.getOrElse(s"Please enter $name: ")
addLine(s"""printf("$prompt");""", id)
val tpe = Try(symTab.getSymbolVar("", name).tpe).toOption.getOrElse(Type.String)
val (format, pointer) = tpe match
case Expression.Type.String => ("%d", name) // array is pointer
case Expression.Type.Integer => ("%d", s"&${name}")
case Expression.Type.Real => ("%d", s"&${name}")
case Expression.Type.Boolean => ("%d", s"&${name}")
case Expression.Type.Void => throw RuntimeException("Void cannot be entered")
addLine(s"""scanf("${format}", ${pointer});""", id)

case Output(id, value, newline) =>
val genValue = parseGenExpr(value)
val text = s"printf($genValue);"
addLine(text, id)

case Block(_, statements) =>
incrIndent()
statements.foreach(genStatement)
decrIndent()

case Return(id, maybeValue) =>
maybeValue.foreach { value =>
val genValue = parseGenExpr(value)
addLine(s"return $genValue;", id)
}

case If(id, condition, trueBlock, falseBlock) =>
val genCond = parseGenExpr(condition)
addLine(s"if ($genCond) {", id)
genStatement(trueBlock)
addLine("} else {", id)
genStatement(falseBlock)
addLine("}", id)

case While(id, condition, block) =>
val genCond = parseGenExpr(condition)
addLine(s"while ($genCond) {", id)
genStatement(block)
addLine("}", id)

case DoWhile(id, condition, block) =>
val genCond = parseGenExpr(condition)
addLine(s"do {", id)
genStatement(block)
addLine(s"} while ($genCond);", id)

case ForLoop(id, varName, start, incr, end, block) =>
val genStart = parseGenExpr(start)
val genIncr = parseGenExpr(incr)
val genEnd = parseGenExpr(end)
addLine(s"for (int $varName = $genStart; i <= $genEnd; i += $genIncr) {", id)
genStatement(block)
addLine("}", id)
case Comment(id, text) =>
addLine(s"/* ${text} */", id)
}
}

import PredefinedFunction.*
override def predefFun(name: String, genArgs: List[String]): String = {
def argOpt(idx: Int) = genArgs.lift(idx).getOrElse("")
PredefinedFunction.withName(name).get match {
case Abs => s"abs(${argOpt(0)})"
case Floor => s"floor(${argOpt(0)})"
case Ceil => s"ceil(${argOpt(0)})"
case RandomInteger => s"abs(${argOpt(0)})" // TODO
case Sin => s"sin(${argOpt(0)})"
case Cos => s"cos(${argOpt(0)})"
case Tan => s"tan(${argOpt(0)})"
case Ln => s"log(${argOpt(0)})"
case Log10 => s"log10(${argOpt(0)})"
case Log2 => s"log10(${argOpt(0)})/log10(2)"
case Sqrt => s"Math.sqrt(${argOpt(0)})"
case Pow => s"Math.pow(${argOpt(0)}, ${argOpt(1)})"
case Length => s"${argOpt(0)}.length()"
case CharAt => s"${argOpt(0)}.charAt(${argOpt(1)})"
case RealToInteger => s"(int)${argOpt(0)}"
case StringToInteger =>
s"""try { Convert.ToInt32(${argOpt(0)}) } catch (FormatException) { 0 }"""
case ReadInput => "TODO"
}
}

override def funCall(name: String, genArgs: List[String]): String =
s""" $name(${genArgs.mkString(", ")}) """.trim

/* TYPE */
private def genType(tpe: Expression.Type): String =
import Expression.Type, Type._
tpe match
case Void => "void"
case Integer => "int"
case Real => "double"
case String => "char"
case Boolean => "bool"

}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ object CodeGeneratorFactory {
case Language.python => PythonGenerator(programAst)
case Language.cSharp => CSharpGenerator(programAst)
case Language.cPLusPLus => CPlusPlusGenerator(programAst)
case Language.c => CGenerator(programAst)
case Language.kotlin => KotlinGenerator(programAst)
case Language.php => PhpGenerator(programAst)
case Language.swift => SwiftGenerator(programAst)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ class JavaGenerator(val programAst: Program) extends CodeGenerator {
case RealToInteger => s"(int)${argOpt(0)}"
case StringToInteger =>
s"""Integer.parseInt(${argOpt(0)})"""
case ReadInput => "Console.ReadLine()"
case ReadInput => "Console.ReadLine()"
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ class KotlinGenerator(val programAst: Program) extends CodeGenerator {
case RealToInteger => s"(int)${argOpt(0)}"
case StringToInteger =>
s"""try { Integer.parseInt(${argOpt(0)}) } catch (NumberFormatException e) { 0 }"""
case ReadInput => "Console.ReadLine()"
case ReadInput => "Console.ReadLine()"
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ enum Language(val name: String, val prism: String):
case scala extends Language("scala", "scala")
case cSharp extends Language("c#", "csharp")
case cPLusPLus extends Language("c++", "cpp")
case c extends Language("c", "c")
case kotlin extends Language("kotlin", "kotlin")
case php extends Language("php", "php")
case swift extends Language("swift", "swift")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ class PhpGenerator(val programAst: Program) extends CodeGenerator {
case RealToInteger => argOpt(0) // ??
case StringToInteger =>
s""" intval(${argOpt(0)}) """.trim
case ReadInput => "Console.ReadLine()"
case ReadInput => "Console.ReadLine()"
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ class ScalaGenerator(val programAst: Program) extends CodeGenerator {
case CharAt => s"${argOpt(0)}.charAt(${argOpt(1)})"
case RealToInteger => s"${argOpt(0)}.toInt"
case StringToInteger => s"${argOpt(0)}.toInt"
case ReadInput => "StdIn.readLine()"
case ReadInput => "StdIn.readLine()"
}
}

Expand Down

0 comments on commit e450a69

Please sign in to comment.