Skip to content

Commit

Permalink
Merge pull request #26 from sacode387/input-prompt-optional
Browse files Browse the repository at this point in the history
Set input prompt and input value echoing as optional, and false by default
  • Loading branch information
sake92 authored Jan 11, 2025
2 parents f0d2036 + d660a9c commit 57f86cb
Show file tree
Hide file tree
Showing 28 changed files with 181 additions and 83 deletions.
3 changes: 2 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ or any "live reload" server...

## Code organization
Project has 2 parts:
- core, which contains all of the logic for the editor/interpreter
- interpreter, which contains all of the logic for the editor/interpreter
- editor, which contains all of the logic for the editor/interpreter
- demo, contains demo code that uses core

Core can be production-optimized with `core/fullLinkJS`.
Expand Down
1 change: 1 addition & 0 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

##

- trim long text with ...
- disable zoom config


8 changes: 8 additions & 0 deletions demo/src/main/public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,14 @@
<input class="flowrun-cb-show-io-btns" type="checkbox">
Show import/export buttons
</label>
<label>
<input class="flowrun-cb-use-input-prompt" type="checkbox">
Use input prompt
</label>
<label>
<input class="flowrun-cb-echo-entered-value" type="checkbox">
Echo entered value
</label>
</div>
</dialog>
</template>
Expand Down
23 changes: 20 additions & 3 deletions editor/src/main/scala/dev/sacode/flowrun/FlowRunEditor.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import java.time.Instant
import java.time.Duration
import java.time.temporal.TemporalUnit
import java.time.temporal.ChronoUnit
import scala.util.control.NonFatal
import scala.compiletime.uninitialized
import scala.concurrent.ExecutionContext.Implicits.global
import scala.scalajs.js
Expand Down Expand Up @@ -55,7 +56,7 @@ class FlowRunEditor(
Program(
AST.newId,
"New Program",
FlowRunConfig("java", true, true),
FlowRunConfig(lang = "java"),
Function(
"main", // don't touch!
"main",
Expand Down Expand Up @@ -339,8 +340,9 @@ class FlowRunEditor(
programModel.ast = loadedProgram
flowRunElements.programNameInput.value = loadedProgram.name
flowrunChannel := FlowRun.Event.FunctionSelected
} catch { e =>
toastify.Toastify(ToastifyOptions("Not a valid program", Color.yellow)).showToast()
} catch {
case NonFatal(_) =>
toastify.Toastify(ToastifyOptions("Not a valid program", Color.yellow)).showToast()
}
}
}
Expand Down Expand Up @@ -434,13 +436,28 @@ class FlowRunEditor(
flowRunElements.showCodeCheckbox.checked = programModel.ast.config.showGenCode
flowRunElements.showDebugVarsCheckbox.checked = programModel.ast.config.showDebugVars
flowRunElements.showIoBtnsCheckbox.checked = programModel.ast.config.showIoBtns
flowRunElements.useInputPromptCheckbox.checked = programModel.ast.config.useInputPrompt
flowRunElements.echoEnteredValueCheckbox.checked = programModel.ast.config.echoEnteredValue

if !fixedLayout then
flowRunElements.showFunctionsCheckbox.oninput = _ => setLayout()
flowRunElements.showCodeCheckbox.oninput = _ => setLayout()

flowRunElements.showDebugVarsCheckbox.oninput = _ => setLayout()
flowRunElements.showIoBtnsCheckbox.oninput = _ => setLayout()

flowRunElements.useInputPromptCheckbox.oninput = _ => {
val newConfig = programModel.ast.config.copy(
useInputPrompt = flowRunElements.useInputPromptCheckbox.checked
)
programModel.setConfig(newConfig)
}
flowRunElements.echoEnteredValueCheckbox.oninput = _ => {
val newConfig = programModel.ast.config.copy(
echoEnteredValue = flowRunElements.echoEnteredValueCheckbox.checked
)
programModel.setConfig(newConfig)
}
}

private def doOnChange(): Unit =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ class FlowRunElements(
mountElem.querySelector(".flowrun-cb-show-debug-vars").asInstanceOf[dom.html.Input]
val showIoBtnsCheckbox: dom.html.Input =
mountElem.querySelector(".flowrun-cb-show-io-btns").asInstanceOf[dom.html.Input]
val useInputPromptCheckbox: dom.html.Input =
mountElem.querySelector(".flowrun-cb-use-input-prompt").asInstanceOf[dom.html.Input]
val echoEnteredValueCheckbox: dom.html.Input =
mountElem.querySelector(".flowrun-cb-echo-entered-value").asInstanceOf[dom.html.Input]

val showConfigButton =
mountElem.querySelector(".flowrun-btn-config").asInstanceOf[dom.html.Element]
Expand Down
35 changes: 19 additions & 16 deletions editor/src/main/scala/dev/sacode/flowrun/edit/CtxMenu.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package dev.sacode.flowrun.edit

import scala.util.control.NonFatal
import org.scalajs.dom
import reactify.*
import ba.sake.tupson.*
Expand All @@ -10,7 +11,6 @@ import dev.sacode.flowrun.FlowRunElements
import dev.sacode.flowrun.FlowRun
import dev.sacode.flowrun.ast.{AST, Statement, Expression}, Statement.*
import dev.sacode.flowrun.Color
import dev.sacode.flowrun.asDyn

class CtxMenu(programModel: ProgramModel, flowRunElements: FlowRunElements, flowrunChannel: Channel[FlowRun.Event]) {

Expand Down Expand Up @@ -111,79 +111,82 @@ class CtxMenu(programModel: ProgramModel, flowRunElements: FlowRunElements, flow
// node buttons
copyButton.addEventListener(
"click",
(event: dom.MouseEvent) =>
(_: dom.MouseEvent) =>
val stmtJson = programModel.findStatement(nodeId).toJson
dom.window.navigator.clipboard.writeText(stmtJson)
flowrunChannel := FlowRun.Event.Deselected
)

deleteButton.addEventListener(
"click",
(event: dom.MouseEvent) => programModel.delete(nodeId)
(_: dom.MouseEvent) => programModel.delete(nodeId)
)

// edge buttons
pasteButton.addEventListener(
"click",
(event: dom.MouseEvent) =>
(_: dom.MouseEvent) =>
dom.window.navigator.clipboard.readText().`then` { copiedText =>
try {
val newStmt = copiedText.parseJson[Statement].duplicated
addStatement(newStmt)
} catch { e =>
toastify.Toastify(ToastifyOptions("Not a valid statement", Color.yellow)).showToast()
} catch {
case NonFatal(_) =>
toastify.Toastify(ToastifyOptions("Not a valid statement", Color.yellow)).showToast()
}
}
)

addDeclareButton.addEventListener(
"click",
(event: dom.MouseEvent) => addStatement(Declare(AST.newId, "x", Expression.Type.Integer, None))
(_: dom.MouseEvent) => addStatement(Declare(AST.newId, "x", Expression.Type.Integer, None))
)

addAssignButton.addEventListener(
"click",
(event: dom.MouseEvent) => addStatement(Assign(AST.newId, "x", "19"))
(_: dom.MouseEvent) => addStatement(Assign(AST.newId, "x", "19"))
)

addInputButton.addEventListener(
"click",
(event: dom.MouseEvent) => addStatement(Input(AST.newId, "x", None))
(_: dom.MouseEvent) =>
val prompt = Option.when(programModel.ast.config.useInputPrompt)("Please enter x:")
addStatement(Input(AST.newId, "x", prompt))
)

addOutputButton.addEventListener(
"click",
(event: dom.MouseEvent) => addStatement(Output(AST.newId, "\"output\"", true))
(_: dom.MouseEvent) => addStatement(Output(AST.newId, "\"output\"", true))
)

addCallButton.addEventListener(
"click",
(event: dom.MouseEvent) => addStatement(Call(AST.newId, "fun1()"))
(_: dom.MouseEvent) => addStatement(Call(AST.newId, "fun1()"))
)

addIfButton.addEventListener(
"click",
(event: dom.MouseEvent) => addStatement(If(AST.newId, "true", Block(AST.newId), Block(AST.newId)))
(_: dom.MouseEvent) => addStatement(If(AST.newId, "true", Block(AST.newId), Block(AST.newId)))
)

addWhileButton.addEventListener(
"click",
(event: dom.MouseEvent) => addStatement(While(AST.newId, "false", Block(AST.newId)))
(_: dom.MouseEvent) => addStatement(While(AST.newId, "false", Block(AST.newId)))
)

addDoWhileButton.addEventListener(
"click",
(event: dom.MouseEvent) => addStatement(DoWhile(AST.newId, "false", Block(AST.newId)))
(_: dom.MouseEvent) => addStatement(DoWhile(AST.newId, "false", Block(AST.newId)))
)

addForLoopButton.addEventListener(
"click",
(event: dom.MouseEvent) => addStatement(ForLoop(AST.newId, "i", "0", "1", "10", Block(AST.newId)))
(_: dom.MouseEvent) => addStatement(ForLoop(AST.newId, "i", "0", "1", "10", Block(AST.newId)))
)

addCommentButton.addEventListener(
"click",
(event: dom.MouseEvent) => addStatement(Comment(AST.newId, "comment"))
(_: dom.MouseEvent) => addStatement(Comment(AST.newId, "comment"))
)
}

Expand Down
12 changes: 7 additions & 5 deletions editor/src/main/scala/dev/sacode/flowrun/edit/OutputArea.scala
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,11 @@ class OutputArea(

val valueInputElem = flowRunElements.newInputText()
val valueBtnElem = flowRunElements.newEnterButton
val promptStr = prompt.getOrElse(s"Please enter '$name': ")
val enterValueDiv = div(
label(cls := "flowrun-user-inputs")(
samp(promptStr),
Option.when(interpreter.programModel.ast.config.useInputPrompt)(
samp(prompt.getOrElse(s"Please enter '$name': "))
),
valueInputElem,
valueBtnElem
)
Expand All @@ -91,9 +92,10 @@ class OutputArea(
resOpt.foreach { value =>
val printVal = if value.tpe == Type.String then s""" "$inputValue" """ else inputValue
flowRunElements.runtimeOutput.removeChild(enterValueDiv)
flowRunElements.runtimeOutput.appendChild(
div(samp(s"You entered $name = $printVal")).render
)
if interpreter.programModel.ast.config.echoEnteredValue then
flowRunElements.runtimeOutput.appendChild(
div(samp(s"You entered $name = $printVal")).render
)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,12 @@ final class StatementEditor(
flowRunElements.stmtOutput.appendChild(
stmtElem(
nameInputElem,
span(" Prompt:"),
promptInputElem
Option.when(programModel.ast.config.useInputPrompt)(
frag(
span(" Prompt:"),
promptInputElem
)
)
).render
)
nameInputElem.focus()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ enum PredefinedFunction(val name: String) {
case Abs extends PredefinedFunction("abs")
case Floor extends PredefinedFunction("floor")
case Ceil extends PredefinedFunction("ceil")
case Sqrt extends PredefinedFunction("sqrt")
case Pow extends PredefinedFunction("pow")
case RandomInteger extends PredefinedFunction("randomInt")
case Sin extends PredefinedFunction("sin")
case Cos extends PredefinedFunction("cos")
case Tan extends PredefinedFunction("tan")
case Ln extends PredefinedFunction("ln")
case Log10 extends PredefinedFunction("log")
case Log2 extends PredefinedFunction("log2")
case Sqrt extends PredefinedFunction("sqrt")
case Pow extends PredefinedFunction("pow")
// strings
case Length extends PredefinedFunction("length")
case CharAt extends PredefinedFunction("charAt")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,12 @@ case class Program(

final case class FlowRunConfig(
lang: String,
showFunctions: Boolean,
showGenCode: Boolean,
showFunctions: Boolean = false,
showGenCode: Boolean = false,
showDebugVars: Boolean = true,
showIoBtns: Boolean = true
showIoBtns: Boolean = true,
useInputPrompt: Boolean = false,
echoEnteredValue: Boolean = false
) derives JsonRW

object FlowRunConfig:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,13 @@ class CGenerator(val programAst: Program) extends CodeGenerator {
addLine(s"$genValue;", id)

case Input(id, name, promptOpt) =>
val prompt = promptOpt.getOrElse(s"Please enter $name: ")
addLine(s"""printf("$prompt");""", id)
promptOpt
.orElse {
Option.when(programAst.config.useInputPrompt)(s"Please enter $name: ")
}
.foreach { prompt =>
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
Expand Down Expand Up @@ -135,7 +140,7 @@ class CGenerator(val programAst: Program) extends CodeGenerator {
val genStart = parseGenExpr(start)
val genIncr = parseGenExpr(incr)
val genEnd = parseGenExpr(end)
addLine(s"for (int $varName = $genStart; i <= $genEnd; i += $genIncr) {", id)
addLine(s"for (int $varName = $genStart; $varName <= $genEnd; $varName += $genIncr) {", id)
genStatement(block)
addLine("}", id)
case Comment(id, text) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,13 @@ class CPlusPlusGenerator(val programAst: Program) extends CodeGenerator {
addLine(s"$genValue;", id)

case Input(id, name, promptOpt) =>
val prompt = promptOpt.getOrElse(s"Please enter $name: ")
addLine(s"""cout << "$prompt";""", id)
promptOpt
.orElse {
Option.when(programAst.config.useInputPrompt)(s"Please enter $name: ")
}
.foreach { prompt =>
addLine(s"""cout << "$prompt";""", id)
}
addLine(s"cin >> $name;", id)

case Output(id, value, newline) =>
Expand Down Expand Up @@ -127,7 +132,7 @@ class CPlusPlusGenerator(val programAst: Program) extends CodeGenerator {
val genStart = parseGenExpr(start)
val genIncr = parseGenExpr(incr)
val genEnd = parseGenExpr(end)
addLine(s"for (int $varName = $genStart; i <= $genEnd; i += $genIncr) {", id)
addLine(s"for (int $varName = $genStart; $varName <= $genEnd; $varName += $genIncr) {", id)
genStatement(block)
addLine("}", id)
case Comment(id, text) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,13 @@ class CSharpGenerator(val programAst: Program) extends CodeGenerator {
addLine(s"$genValue;", id)

case Input(id, name, promptOpt) =>
val prompt = promptOpt.getOrElse(s"Please enter $name: ")
addLine(s"""Console.Write("$prompt");""", id)

promptOpt
.orElse {
Option.when(programAst.config.useInputPrompt)(s"Please enter $name: ")
}
.foreach { prompt =>
addLine(s"""Console.Write("$prompt");""", id)
}
val symOpt = Try(symTab.getSymbolVar("", name)).toOption
val readFun = readFunction(symOpt.map(_.tpe))
addLine(s"$name = $readFun;", id)
Expand Down Expand Up @@ -132,7 +136,7 @@ class CSharpGenerator(val programAst: Program) extends CodeGenerator {
val genStart = parseGenExpr(start)
val genIncr = parseGenExpr(incr)
val genEnd = parseGenExpr(end)
addLine(s"for (int $varName = $genStart; i <= $genEnd; i += $genIncr) {", id)
addLine(s"for (int $varName = $genStart; $varName <= $genEnd; $varName += $genIncr) {", id)
genStatement(block)
addLine("}", id)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ trait CodeGenerator {
terms.mkString(" ")
}
private def genTerm(term: Term): String = {
// TODO handle string concatenation
// TODO handle string concatenation, lang specific...
val factors =
List(genFactor(term.factor)) ++ term.factors.map(fo => s""" ${fo.op.text} ${genFactor(fo.factor)} """.trim)
factors.mkString(" ")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,13 @@ class GolangGenerator(val programAst: Program) extends CodeGenerator {
addLine(genValue, id)

case Input(id, name, promptOpt) =>
val prompt = promptOpt.getOrElse(s"Please enter $name: ")
addLine(s"""fmt.Print("$prompt")""", id)

promptOpt
.orElse {
Option.when(programAst.config.useInputPrompt)(s"Please enter $name: ")
}
.foreach { prompt =>
addLine(s"""fmt.Print("$prompt")""", id)
}
val symOpt = Try(symTab.getSymbolVar("", name)).toOption
val readFun = readFunction(symOpt.map(_.tpe))
addLine(s"$name = $readFun", id)
Expand Down
Loading

0 comments on commit 57f86cb

Please sign in to comment.