Skip to content

Instantly share code, notes, and snippets.

@hyperswine
Created December 11, 2023 18:06
Show Gist options
  • Save hyperswine/c360392b7db810847a14ac28b0e578e2 to your computer and use it in GitHub Desktop.
Save hyperswine/c360392b7db810847a14ac28b0e578e2 to your computer and use it in GitHub Desktop.
example-scala3
import scala.util.parsing.combinator._
sealed trait WorkflowToken
case class IDENTIFIER(str: String) extends WorkflowToken
case class LITERAL(str: String) extends WorkflowToken
case class INDENTATION(spaces: Int) extends WorkflowToken
case object EXIT extends WorkflowToken
case object READINPUT extends WorkflowToken
case object CALLSERVICE extends WorkflowToken
case object SWITCH extends WorkflowToken
case object OTHERWISE extends WorkflowToken
case object COLON extends WorkflowToken
case object ARROW extends WorkflowToken
case object EQUALS extends WorkflowToken
case object COMMA extends WorkflowToken
case object INDENT extends WorkflowToken
case object DEDENT extends WorkflowToken
import scala.util.chaining._
object WorkflowLexer extends RegexParsers:
override def skipWhitespace = true
override val whiteSpace = "[ \t\r\f]+".r
def identifier = "[a-zA-Z_][a-zA-Z0-9_]*".r ^^ { IDENTIFIER.apply }
def literal: Parser[LITERAL] = "[^\"]*".r ^^ { str =>
str.substring(1, str.length - 1) |> LITERAL.apply
}
def indentation = "\n[ ]*".r ^^ { whitespace =>
whitespace.length - 1 |> INDENTATION.apply
}
def exit = "exit" ^^ (_ => EXIT)
def readInput = "read input" ^^ (_ => READINPUT)
def callService = "call service" ^^ (_ => CALLSERVICE)
def switch = "switch" ^^ (_ => SWITCH)
def otherwise = "otherwise" ^^ (_ => OTHERWISE)
def colon = ":" ^^ (_ => COLON)
def arrow = "->" ^^ (_ => ARROW)
def equals = "==" ^^ (_ => EQUALS)
def comma = "," ^^ (_ => COMMA)
def processIndentations(
tokens: List[WorkflowToken],
indents: List[Int] = List(0)
): List[WorkflowToken] =
tokens.headOption match
case Some(INDENTATION(spaces)) if spaces > indents.head =>
INDENT :: processIndentations(tokens.tail, spaces :: indents)
case Some(INDENTATION(spaces)) if spaces < indents.head =>
val (dropped, kept) = indents.partition(_ > spaces)
(dropped map (_ => DEDENT)) ::: processIndentations(tokens.tail, kept)
case Some(INDENTATION(spaces)) if spaces == indents.head =>
processIndentations(tokens.tail, indents)
case Some(token) => token :: processIndentations(tokens.tail, indents)
case None => indents.filter(_ > 0).map(_ => DEDENT)
def tokens =
((exit | readInput | callService | switch | otherwise | colon | arrow | equals | comma | literal | identifier | indentation) |> rep1 |> phrase) ^^ {
rawtokens => processIndentations(rawtokens)
}
def apply(code: String) = parse(tokens, code) match
case NoSuccess(msg, next) => Left(WorkflowLexerError(msg))
case Success(result, next) => Right(result)
case _ => Left(WorkflowLexerError("Other error"))
val code = """
read input name, country
switch:
country == "PT" ->
call service "A"
exit
otherwise ->
call service "B"
switch:
name == "unknown" ->
exit
otherwise ->
call service "C"
exit
"""
WorkflowLexer(code)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment