Skip to content

Commit 7212bf1

Browse files
committed
Minor Tweaks
1 parent 03fa130 commit 7212bf1

File tree

9 files changed

+62
-57
lines changed

9 files changed

+62
-57
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,12 +160,13 @@ The parent class additionally implements methods to iterate through matches and
160160
* `*`: Returns a parser that parses the entire input (basically making parseAllValues the result)
161161
* `+`: Like `*` except it fails if there are no matches
162162

163-
There are currently 5 primary parser classes:
163+
There are currently 6 primary parser classes:
164164
* ChunkParser: Takes a function which returns an `Option`
165165
* PartialParser: Similar to a ChunkParser except it takes a PartialFunction
166166
* RecurParser: This one is for parsing to tree structures, currently used in Unlambda. It takes a `recur` function that decides when to go down a level, a `collect` function to turn a set of leaves into a node, and another parser for the non-branching tokens.
167167
* RegexParser: This takes a regex and a function to turn a match into the output. This one lets me condense a lot of parsers into a single line, which makes me giddy.
168168
* ArbitraryRecurParser: This one's called "arbitrary" because the depth isn't constant. It's given a function that can either parse the next token or tell it to go up or down a level.
169+
* ElementwiseParser: This one parses a sequence element by element
169170

170171
### How the Interface Works
171172
It's always a challenge to handle UI functionally. Everything behind the scenes is fair game, but actually receiving input from and sending output to the user is by definition a side-effect.

src/main/scala/grass/Grass.scala

Lines changed: 15 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,25 @@ object Grass extends Interpreter{
1010
val name: String = "Grass"
1111

1212
val grassParser: EsoParser[String, Vector[Expr]] = {
13-
val absReg = raw"""(w*)([Ww]*)""".r
1413
@tailrec
1514
def absArity(n: Int, p: Vector[Expr]): Vector[Expr] = {
1615
if(n > 0) absArity(n - 1, Vector(AbsExpr(p)))
1716
else p}
18-
val appParser = RegexParser(raw"""(W+)(w+)""")(m => AppExpr(m.group(1).length, m.group(2).length))
19-
RegexParser(raw"""([Ww]+)"""){m =>
20-
m.group(1) match{
21-
case absReg(arw, apps) => absArity(arw.length, appParser.parseAllValues(apps))}}}
22-
23-
def apply(config: Config)(progRaw: String): Try[Seq[Char] => LazyList[Char]] = Try{eval(parse(progRaw))}
17+
val appParser = RegexParser(raw"""(W+)(w+)""")(m => AppExpr(m.group(1).length, m.group(2).length)).*
18+
RegexParser(raw"""(w*)([Ww]*)""")(m => absArity(m.group(1).length, appParser.parseOne(m.group(2)))).*
19+
.map{res =>
20+
res.flatten match{
21+
case as :+ (a: AbsExpr) => as :+ a :+ AppExpr(1, 1)
22+
case as => as}}
23+
.withConditioning{str =>
24+
filterChars(str
25+
.replaceAllLiterally("\uff37", "W")
26+
.replaceAllLiterally("\uff57", "w")
27+
.replaceAllLiterally("\uFF56", "v")
28+
.replaceAllLiterally("\uFF36", "v"),
29+
"vwW").dropWhile(_ != 'w')}}
30+
31+
def apply(config: Config)(progRaw: String): Try[Seq[Char] => LazyList[Char]] = Try{eval(grassParser.parseOne(progRaw))}
2432

2533
def eval(prog: Vector[Expr]): Seq[Char] => LazyList[Char] = {
2634
@tailrec
@@ -30,19 +38,6 @@ object Grass extends Interpreter{
3038
case _ => edo(state.run())}
3139
inputs => LazyList.unfold(RunState(prog, Env(Vector(Out, Succ, CharFun('w'), In)), inputs, HaltState): State)(edo)}
3240

33-
def parse(progRaw: String): Vector[Expr] = {
34-
val conditioned = filterChars(progRaw
35-
.replaceAllLiterally("\uff37", "W")
36-
.replaceAllLiterally("\uff57", "w")
37-
.replaceAllLiterally("\uFF56", "v")
38-
.replaceAllLiterally("\uFF36", "v"),
39-
"vwW")
40-
.dropWhile(_ != 'w')
41-
42-
grassParser.parseAllValues(conditioned).flatten match{
43-
case as :+ (a: AbsExpr) => as :+ a :+ AppExpr(1, 1)
44-
case as => as}}
45-
4641
case class Env(stk: Vector[Func]){
4742
def apply(i: Int): Func = stk(i)
4843
def head: Func = stk.head
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package parsers
2+
3+
case class AfterParser[A, B](parser: EsoParser[A, B]) extends EsoParser[A, A]{
4+
def apply(inp: A): EsoParseRes[A, A] = parser(inp) mapAll {case (_, rem, s, e) => (rem, rem, s, e)}
5+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package parsers
2+
3+
case class ConditionalParser[A, B](parser: EsoParser[A, B], cond: B => Boolean) extends EsoParser[A, B]{
4+
def apply(inp: A): EsoParseRes[A, B] = {
5+
val res = parser(inp)
6+
if(res.passed && cond(res.get)) res
7+
else EsoParseFail}
8+
}

src/main/scala/parsers/EsoParser.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,9 @@ abstract class EsoParser[A, B] extends (A => EsoParseRes[A, B]) with EsoObj{
8686

8787
def map[C](f: B => C): EsoParser[A, C] = MappedParser(this, f)
8888
def withConditioning(f: A => A): EsoParser[A, B] = ConditioningParser(this, f)
89+
def onlyIf(cond: B => Boolean): ConditionalParser[A, B] = ConditionalParser(this, cond)
8990
def withErrors: ErrorParser[A, B] = ErrorParser(this)
91+
def after: AfterParser[A, B] = AfterParser(this)
9092

9193
def >>[C](q: EsoParser[B, C]): EsoParser[A, C] = ChainedParser(this, q)
9294

src/main/scala/thue/Thue.scala

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
11
package thue
22

3-
import common.{Config, EsoExcep, Interpreter}
3+
import common.{Config, Interpreter}
44
import parsers.{EsoParser, RegexParser}
55

66
import scala.annotation.tailrec
7-
import scala.util.matching.Regex
8-
import scala.util.{Failure, Success, Try}
7+
import scala.util.Try
98

109
object Thue extends Interpreter{
1110
val name: String = "Thue"
1211

13-
val initReg: Regex = raw"""(?s)(.*)\n\s*::=\s*\n(.*)\z""".r
14-
val ruleParser: EsoParser[String, (String, String)] = RegexParser(raw"""(?m)^(.*)::=(.*)$$""".r){ m => (m.group(1), m.group(2))}
12+
val thueParser: EsoParser[String, (Vector[(String, String)], String)] = {
13+
val spcReg = """\s*""".r
14+
val pairParser = RegexParser("""(?m)^(.*)::=(.*)$$""")(m => (m.group(1), m.group(2)))
15+
val ruleParser = pairParser.onlyIf{case (a, b) => !spcReg.matches(a ++ b)}
16+
ruleParser.* <&> pairParser.after}
1517

16-
def apply(config: Config)(progRaw: String): Try[Seq[Char] => LazyList[Char]] = parse(progRaw) map{case (init, prog) => thi(init, prog, config.rands)}
18+
def apply(config: Config)(progRaw: String): Try[Seq[Char] => LazyList[Char]] = thueParser(progRaw).toTry() map{case (prog, init) => thi(init, prog, config.rands)}
1719

1820
def thi(init: String, prog: Vector[(String, String)], initRands: LazyList[Int]): Seq[Char] => LazyList[Char] = {
1921
def collapse(inp: Seq[Char]): Seq[String] = LazyList.unfold(inp){lst =>
@@ -35,9 +37,4 @@ object Thue extends Interpreter{
3537
if(v.startsWith("~")) Some((v.tail, (ac.replaceAllLiterally(k, ""), inp, rs)))
3638
else tdo(ac.replaceAllLiterally(k, v), inp, rs)}}}
3739
inputs => LazyList.unfold((init, collapse(inputs), initRands)){case (ac, inp, rs) => tdo(ac, inp, rs)}.flatten}
38-
39-
def parse(progRaw: String): Try[(String, Vector[(String, String)])] = {
40-
progRaw match{
41-
case initReg(rls, init) => Success((init, ruleParser.parseAllValues(rls)))
42-
case _ => Failure(EsoExcep("Malformed Program"))}}
4340
}

src/main/scala/ui/EsoCommandParser.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ object EsoCommandParser extends EsoObj{
99
val cmdParser: EsoParser[String, (String, immutable.HashMap[String, String])] = {
1010
val opParser = RegexParser(raw"""^(\S+)\s*""")(m => m.group(1))
1111
val argParser = RegexParser(raw"""[^-]*-(\S*) (\S*)""")(m => (m.group(1), m.group(2)))
12-
opParser <&> argParser.*.map(mkMap(_))}
12+
opParser <&> (argParser.* map (mkMap(_)))}
1313

1414
def apply(str: String): Option[(String, immutable.HashMap[String, String])] = cmdParser(str).toOption
1515
}

src/main/scala/volatile/Volatile.scala

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package volatile
22

33
import common.{Config, Interpreter}
4-
import parsers.{EsoParser, PartialParser}
4+
import parsers.{EsoParser, PartialElementwiseParser, PartialParser}
55
import spire.math.SafeLong
66

77
import scala.annotation.tailrec
@@ -10,20 +10,17 @@ import scala.util.{Success, Try}
1010
object Volatile extends Interpreter{
1111
val name: String = "Volatile"
1212

13-
val volParser: EsoParser[Vector[Char], VOP] = {
14-
PartialParser.simple{
15-
case c +: cs =>
16-
val op = c match{
17-
case '~' => PUSH
18-
case '+' => ADD
19-
case '-' => SUBT
20-
case '*' => MULT
21-
case '/' => DIV
22-
case ':' => DUP
23-
case '.' => OUT
24-
case '(' => LSTART(-1)
25-
case ')' => LEND(-1)}
26-
(op, cs)}}
13+
val volParser: EsoParser[Seq[Char], VOP] = {
14+
PartialElementwiseParser[Char, VOP]{
15+
case '~' => PUSH
16+
case '+' => ADD
17+
case '-' => SUBT
18+
case '*' => MULT
19+
case '/' => DIV
20+
case ':' => DUP
21+
case '.' => OUT
22+
case '(' => LSTART(-1)
23+
case ')' => LEND(-1)}}
2724

2825
def apply(config: Config)(progRaw: String): Try[Seq[Char] => LazyList[Char]] = {
2926
val prog = parse(progRaw)

src/main/scala/wordlang/WordLang.scala

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,14 @@ object WordLang extends Interpreter{
3333

3434
val baseParser = ibvParser <+> ivbParser <+> lblParser <+> jmpParser <+> charParser
3535
val parParser = RegexParser[WOP](raw"""\(([^\(\)]*)\)""")(m => PrintNum(baseParser.parseAllValues(m.group(1))))
36-
baseParser <+> parParser}
36+
(baseParser <+> parParser)
37+
.withConditioning{str =>
38+
val escaped = escapeReg
39+
.replaceAllIn(str, m => (-m.group(1).head).toChar.toString)
40+
commentReg
41+
.replaceAllIn(escaped, "")
42+
.linesIterator
43+
.mkString(" ")}}
3744

3845
def apply(config: Config)(progRaw: String): Try[Seq[Char] => LazyList[Char]] = Try{parse(progRaw)} map{
3946
case (prog, jumps) =>
@@ -68,20 +75,13 @@ object WordLang extends Interpreter{
6875
case (cur, heap, mode, src, inp) => rdo(cur, heap, mode, src, inp)}.flatten}
6976

7077
def parse(progRaw: String): (Vector[WOP], immutable.HashMap[String, Int]) = {
71-
val escaped = escapeReg
72-
.replaceAllIn(progRaw, m => (-m.group(1).head).toChar.toString)
73-
val uncommented = commentReg
74-
.replaceAllIn(escaped, "")
75-
.linesIterator
76-
.mkString(" ")
77-
7878
@tailrec
7979
def pdo(src: Seq[WOP], ac: Vector[WOP], acm: immutable.HashMap[String, Int]): (Vector[WOP], immutable.HashMap[String, Int]) = src match{
8080
case op +: ops => op match{
8181
case JumpLabel(nam) => pdo(ops, ac, acm + ((nam, ac.size)))
8282
case _ => pdo(ops, ac :+ op, acm)}
8383
case _ => (ac, acm)}
84-
pdo(wordLangParser.parseAllValuesLazy(uncommented), Vector(), immutable.HashMap())}
84+
pdo(wordLangParser.parseAllValuesLazy(progRaw), Vector(), immutable.HashMap())}
8585

8686
trait WOP
8787
case class CharOp(c: Char) extends WOP

0 commit comments

Comments
 (0)