Add 'dotscape/' from commit 'f306501a68a51b634e895c5fdac70788ae899d75'
git-subtree-dir: dotscape git-subtree-mainline:6b91d64fc3git-subtree-split:f306501a68
This commit is contained in:
44
dotscape/fcore/parser/all-dfa.sml
Normal file
44
dotscape/fcore/parser/all-dfa.sml
Normal file
@@ -0,0 +1,44 @@
|
||||
structure AllDfa =
|
||||
struct
|
||||
type t =
|
||||
{ curInt: int
|
||||
, curSpace: int
|
||||
, curBrace: int
|
||||
, lastInt: int
|
||||
, lastSpace: int
|
||||
, lastBrace: int
|
||||
}
|
||||
|
||||
val initial: t =
|
||||
{ curInt = IntDfa.start
|
||||
, curSpace = SpaceDfa.start
|
||||
, curBrace = BraceDfa.start
|
||||
, lastInt = ~1
|
||||
, lastSpace = ~1
|
||||
, lastBrace = ~1
|
||||
}
|
||||
|
||||
fun areAllDead ({curInt, curSpace, curBrace, ...}: t) =
|
||||
curInt = 0 andalso curSpace = 0 andalso curBrace = 0
|
||||
|
||||
fun update (chr, dfa, pos) =
|
||||
let
|
||||
val {curInt, curSpace, curBrace, lastInt, lastBrace, lastSpace} = dfa
|
||||
|
||||
val curInt = IntDfa.next (curInt, chr)
|
||||
val curSpace = SpaceDfa.next (curSpace, chr)
|
||||
val curBrace = BraceDfa.next (curBrace, chr)
|
||||
|
||||
val lastInt = if IntDfa.isFinal curInt then pos else lastInt
|
||||
val lastSpace = if SpaceDfa.isFinal curSpace then pos else lastSpace
|
||||
val lastBrace = if BraceDfa.isFinal curBrace then pos else lastBrace
|
||||
in
|
||||
{ curInt = curInt
|
||||
, curSpace = curSpace
|
||||
, curBrace = curBrace
|
||||
, lastInt = lastInt
|
||||
, lastBrace = lastBrace
|
||||
, lastSpace = lastSpace
|
||||
}
|
||||
end
|
||||
end
|
||||
32
dotscape/fcore/parser/brace-dfa.sml
Normal file
32
dotscape/fcore/parser/brace-dfa.sml
Normal file
@@ -0,0 +1,32 @@
|
||||
structure BraceDfa =
|
||||
struct
|
||||
val dead = 0
|
||||
val start = 1
|
||||
val final = 2
|
||||
|
||||
fun makeStart i =
|
||||
let
|
||||
val chr = Char.chr i
|
||||
in
|
||||
if chr = #"{" orelse chr = #"}" orelse chr = #"[" orelse chr = #"]" then
|
||||
final
|
||||
else
|
||||
dead
|
||||
end
|
||||
|
||||
val deadTable = SpaceDfa.deadTable
|
||||
val startTable = Vector.tabulate (255, makeStart)
|
||||
val finalTable = deadTable
|
||||
|
||||
val tables = #[deadTable, startTable, finalTable]
|
||||
|
||||
fun isFinal state = state = final
|
||||
|
||||
fun next (state, chr) =
|
||||
let
|
||||
val table = Vector.sub (tables, state)
|
||||
val idx = Char.ord chr
|
||||
in
|
||||
Vector.sub (table, idx)
|
||||
end
|
||||
end
|
||||
27
dotscape/fcore/parser/int-dfa.sml
Normal file
27
dotscape/fcore/parser/int-dfa.sml
Normal file
@@ -0,0 +1,27 @@
|
||||
structure IntDfa =
|
||||
struct
|
||||
val dead = 0
|
||||
val start = 1
|
||||
val final = 2
|
||||
|
||||
fun makeStart i =
|
||||
let val chr = Char.chr i
|
||||
in if Char.isDigit chr then final else dead
|
||||
end
|
||||
|
||||
val deadTable = Vector.tabulate (255, fn _ => dead)
|
||||
val startTable = Vector.tabulate (255, makeStart)
|
||||
val finalTable = startTable
|
||||
|
||||
val tables = #[deadTable, startTable, finalTable]
|
||||
|
||||
fun isFinal state = state = final
|
||||
|
||||
fun next (state, chr) =
|
||||
let
|
||||
val table = Vector.sub (tables, state)
|
||||
val idx = Char.ord chr
|
||||
in
|
||||
Vector.sub (table, idx)
|
||||
end
|
||||
end
|
||||
59
dotscape/fcore/parser/lexer.sml
Normal file
59
dotscape/fcore/parser/lexer.sml
Normal file
@@ -0,0 +1,59 @@
|
||||
structure Lexer =
|
||||
struct
|
||||
structure T = Tokens
|
||||
|
||||
fun validMin (a, b) =
|
||||
if a = ~1 then b else if b = ~1 then a else Int.min (a, b)
|
||||
|
||||
fun addToken (acc, dfa: AllDfa.t, str, finish) =
|
||||
let
|
||||
val {lastInt, lastSpace, lastBrace, ...} = dfa
|
||||
val min = validMin (lastInt, lastSpace)
|
||||
val min = validMin (min, lastBrace)
|
||||
in
|
||||
if min = ~1 then
|
||||
NONE
|
||||
else if min = lastSpace then
|
||||
SOME (lastSpace, acc)
|
||||
else
|
||||
let
|
||||
val str = String.substring (str, min, finish - min + 1)
|
||||
in
|
||||
if min = lastInt then
|
||||
case Int.fromString str of
|
||||
SOME int => SOME (lastInt, T.INT int :: acc)
|
||||
| NONE => NONE
|
||||
else if min = lastBrace then
|
||||
if str = "{" then SOME (lastBrace, T.L_BRACE :: acc)
|
||||
else if str = "}" then SOME (lastBrace, T.R_BRACE :: acc)
|
||||
else if str = "[" then SOME (lastBrace, T.L_BRACKET :: acc)
|
||||
else if str = "]" then SOME (lastBrace, T.R_BRACKET :: acc)
|
||||
else NONE
|
||||
else
|
||||
NONE
|
||||
end
|
||||
end
|
||||
|
||||
fun scanStep (pos, str, acc, dfa, finish) =
|
||||
if pos < 0 orelse AllDfa.areAllDead dfa then
|
||||
addToken (acc, dfa, str, finish)
|
||||
else
|
||||
let
|
||||
val chr = String.sub (str, pos)
|
||||
val dfa = AllDfa.update (chr, dfa, pos)
|
||||
in
|
||||
if AllDfa.areAllDead dfa then addToken (acc, dfa, str, finish)
|
||||
else scanStep (pos - 1, str, acc, dfa, finish)
|
||||
end
|
||||
|
||||
fun scanLoop (pos, str, acc) =
|
||||
if pos < 0 then
|
||||
SOME acc
|
||||
else
|
||||
case scanStep (pos, str, acc, AllDfa.initial, pos) of
|
||||
SOME (pos, acc) => scanLoop (pos - 1, str, acc)
|
||||
| NONE => NONE
|
||||
|
||||
fun scan str =
|
||||
scanLoop (String.size str - 1, str, [])
|
||||
end
|
||||
36
dotscape/fcore/parser/parse-grid.sml
Normal file
36
dotscape/fcore/parser/parse-grid.sml
Normal file
@@ -0,0 +1,36 @@
|
||||
structure ParseGrid =
|
||||
struct
|
||||
fun make (canvasWidth, canvasHeight) =
|
||||
let
|
||||
val maxPoints = Int.max (canvasWidth, canvasHeight)
|
||||
val emptyYAxis = Vector.tabulate (maxPoints, fn _ =>
|
||||
{r = 0, g = 0, b = 0, a = 0})
|
||||
in
|
||||
Vector.tabulate (maxPoints, fn _ => emptyYAxis)
|
||||
end
|
||||
|
||||
local
|
||||
fun loopY (yAxis, x, ex, y, ey, colour) =
|
||||
if y > ey orelse y >= Vector.length yAxis then
|
||||
yAxis
|
||||
else
|
||||
let val yAxis = Vector.update (yAxis, y, colour)
|
||||
in loopY (yAxis, x, ex, y + 1, ey, colour)
|
||||
end
|
||||
|
||||
fun loopX (grid, x, ex, y, ey, colour) =
|
||||
if x > ex orelse x >= Vector.length grid then
|
||||
grid
|
||||
else
|
||||
let
|
||||
val yAxis = Vector.sub (grid, x)
|
||||
val yAxis = loopY (yAxis, x, ex, y, ey, colour)
|
||||
val grid = Vector.update (grid, x, yAxis)
|
||||
in
|
||||
loopX (grid, x + 1, ex, y, ey, colour)
|
||||
end
|
||||
in
|
||||
fun applyItem (grid, x, y, ex, ey, colour) =
|
||||
loopX (grid, x, ex, y, ey, colour)
|
||||
end
|
||||
end
|
||||
21
dotscape/fcore/parser/parser.md
Normal file
21
dotscape/fcore/parser/parser.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# Parsing
|
||||
|
||||
The parsing functionality is for saving and loading from a custom file format.
|
||||
|
||||
The BNF for the custom file format is below.
|
||||
|
||||
Terminals are surrounded by `**` to the left and right. (rule)+ means "1 or more".
|
||||
|
||||
```
|
||||
int ::= (0-9)+
|
||||
|
||||
item ::= **{** int int int int int int int int **}**
|
||||
|
||||
layer ::= **[** item **]**
|
||||
|
||||
layer_tree ::= int int **{** (layer)* **}**
|
||||
```
|
||||
|
||||
The first two `int`s in the `layer_tree` always follow the order: `canvasWidth canvasHeight`.
|
||||
|
||||
The large number of `int`s in the `item` always follow the order: `x y ex ey r g b a`.
|
||||
82
dotscape/fcore/parser/parser.sml
Normal file
82
dotscape/fcore/parser/parser.sml
Normal file
@@ -0,0 +1,82 @@
|
||||
structure Parser =
|
||||
struct
|
||||
structure T = Tokens
|
||||
|
||||
fun parseItem (tokens, grid) =
|
||||
case tokens of
|
||||
T.L_BRACE ::
|
||||
T.INT x ::
|
||||
T.INT y ::
|
||||
T.INT ex ::
|
||||
T.INT ey ::
|
||||
T.INT r :: T.INT g :: T.INT b :: T.INT a :: T.R_BRACE :: tl =>
|
||||
let
|
||||
val colour = {r = r, g = g, b = b, a = a}
|
||||
val grid = ParseGrid.applyItem (grid, x, y, ex, ey, colour)
|
||||
in
|
||||
SOME (tl, grid)
|
||||
end
|
||||
| _ => NONE
|
||||
|
||||
(* note to be careful of:
|
||||
* - startParseItems returns NONE if there are no items found,
|
||||
* because we have not found a single item yet.
|
||||
*
|
||||
* - loopParseItems returns SOME if there are no items found,
|
||||
* because this function is called after we have parsed at least one item.
|
||||
* *)
|
||||
fun loopParseItems (tokens, grid) =
|
||||
case parseItem (tokens, grid) of
|
||||
SOME (tokens, grid) => loopParseItems (tokens, grid)
|
||||
| NONE => SOME (tokens, grid)
|
||||
|
||||
fun startParseItems (tokens, grid) =
|
||||
case parseItem (tokens, grid) of
|
||||
SOME (tokens, grid) => loopParseItems (tokens, grid)
|
||||
| NONE => NONE
|
||||
|
||||
fun parseLayer (tokens, canvasWidth, canvasHeight, tree, counter) =
|
||||
case tokens of
|
||||
T.L_BRACKET :: tl =>
|
||||
let
|
||||
val grid = ParseGrid.make (canvasWidth, canvasHeight)
|
||||
in
|
||||
case startParseItems (tl, grid) of
|
||||
SOME (T.R_BRACKET :: tl, grid) =>
|
||||
let val tree = LayerTree.insert (counter, grid, tree)
|
||||
in SOME (tl, tree)
|
||||
end
|
||||
| SOME _ => NONE
|
||||
| NONE => NONE
|
||||
end
|
||||
| _ => NONE
|
||||
|
||||
fun parseLayerLoop (tokens, canvasWidth, canvasHeight, tree, counter) =
|
||||
case parseLayer (tokens, canvasWidth, canvasHeight, tree, counter) of
|
||||
SOME (tl, tree) =>
|
||||
parseLayerLoop (tl, canvasWidth, canvasHeight, tree, counter + 1)
|
||||
| NONE => SOME (tokens, tree)
|
||||
|
||||
fun startParseLayer (tokens, canvasWidth, canvasHeight, tree) =
|
||||
case parseLayer (tokens, canvasWidth, canvasHeight, tree, 1) of
|
||||
SOME (tl, tree) => parseLayerLoop (tl, canvasWidth, canvasHeight, tree, 2)
|
||||
| NONE => NONE
|
||||
|
||||
fun parse string =
|
||||
case Lexer.scan string of
|
||||
SOME tokens =>
|
||||
(case tokens of
|
||||
T.INT canvasWidth :: T.INT canvasHeight :: T.L_BRACE :: tl =>
|
||||
let
|
||||
val maxSide = Int.max (canvasWidth, canvasHeight)
|
||||
val tree = LayerTree.init maxSide
|
||||
in
|
||||
case startParseLayer (tl, canvasWidth, canvasHeight, tree) of
|
||||
SOME ([T.R_BRACE], tree) =>
|
||||
SOME (canvasWidth, canvasHeight, tree)
|
||||
| SOME _ => NONE
|
||||
| NONE => NONE
|
||||
end
|
||||
| _ => NONE)
|
||||
| NONE => NONE
|
||||
end
|
||||
29
dotscape/fcore/parser/space-dfa.sml
Normal file
29
dotscape/fcore/parser/space-dfa.sml
Normal file
@@ -0,0 +1,29 @@
|
||||
structure SpaceDfa =
|
||||
struct
|
||||
val dead = 0
|
||||
val start = 1
|
||||
val final = 2
|
||||
|
||||
fun makeDead _ = 0
|
||||
|
||||
fun makeStart i =
|
||||
let val chr = Char.chr i
|
||||
in if Char.isSpace chr then final else dead
|
||||
end
|
||||
|
||||
val deadTable = Vector.tabulate (255, makeDead)
|
||||
val startTable = Vector.tabulate (255, makeStart)
|
||||
val finalTable = startTable
|
||||
|
||||
val tables = #[deadTable, startTable, finalTable]
|
||||
|
||||
fun isFinal state = state = final
|
||||
|
||||
fun next (state, chr) =
|
||||
let
|
||||
val table = Vector.sub (tables, state)
|
||||
val idx = Char.ord chr
|
||||
in
|
||||
Vector.sub (table, idx)
|
||||
end
|
||||
end
|
||||
2
dotscape/fcore/parser/tokens.sml
Normal file
2
dotscape/fcore/parser/tokens.sml
Normal file
@@ -0,0 +1,2 @@
|
||||
structure Tokens =
|
||||
struct datatype t = L_BRACE | R_BRACE | L_BRACKET | R_BRACKET | INT of int end
|
||||
Reference in New Issue
Block a user