Skip to content

Commit

Permalink
Support switch statements (#132)
Browse files Browse the repository at this point in the history
This is relatively simple and straightforward.

Note that this is much more permissive than GLSL compilers seem to be;
for example I'm pretty sure this will allow much more in a case label
than it should (I think a literal int must come after). But I don't
think it will cause the minifier to emit invalid GLSL unless the input
was invalid already.

It's actually somewhat unclear what the right grammar even is here.
Looking at the 4.5 spec linked in #18 -- section 9 (the normative
grammar), page 205 says that a `case_label` is `CASE expression COLON`,
but from testing with an actual compiler it looks like it needs to be a
literal integer. Also the definition of `switch_statement_list` doesn't
include `case_label`.  So by my reading that's a bug in the spec (or at
least a place that compilers are quite reasonably more restrictive) so
even if we did want to be perfectly correct here I'm not sure what
exactly that looks like :)

Fixes #18.

* Also fix newline handling in IndentedText output format

This oversight was causing the format to print a literal '\n' instead of
a newline. Add the correct pattern match, and also remove the wildcard
match to prevent similar bugs in the future.
  • Loading branch information
jwatzman committed Apr 27, 2022
1 parent 9f5a5c0 commit 342adde
Show file tree
Hide file tree
Showing 8 changed files with 137 additions and 4 deletions.
13 changes: 13 additions & 0 deletions src/ast.fs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,11 @@ and Stmt =
| DoWhile of Expr * Stmt
| Jump of JumpKeyword * Expr option (*break, continue, return (expr)?, discard*)
| Verbatim of string
| Switch of Expr * (CaseLabel * Stmt list) list

and CaseLabel =
| Case of Expr
| Default

and FunctionType = {
retType: Type (*return*)
Expand Down Expand Up @@ -184,6 +189,14 @@ let rec mapStmt env i =
| Jump(k, e) ->
env, Jump (k, Option.map (mapExpr env) e)
| Verbatim _ as v -> env, v
| Switch(e, cl) ->
let mapLabel = function
| Case e -> Case (mapExpr env e)
| Default -> Default
let mapCase (l, sl) =
let _, sl = foldList env mapStmt sl
(mapLabel l, sl)
env, Switch (mapExpr env e, List.map mapCase cl)
let env, res = aux i
env, env.fStmt res

Expand Down
14 changes: 13 additions & 1 deletion src/parse.fs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ open Options.Globals
module private ParseImpl =

// TODO: true, false
// TODO: switch case

open FParsec.Primitives
open FParsec.CharParsers
Expand Down Expand Up @@ -280,6 +279,18 @@ module private ParseImpl =
let list = many statement |>> Ast.Block
between (ch '{') (ch '}') list

let caseLabel =
let keywCase = keyword "case" >>. expr |>> (fun e -> Ast.Case e)
let keywDefault = keyword "default" |>> (fun _ -> Ast.Default)
attempt keywCase <|> keywDefault .>> ch ':'

let case =
pipe2 caseLabel (many (attempt statement)) (fun l sl -> (l, sl))

let switch =
let body = between (ch '{') (ch '}') (many case)
pipe2 (keyword "switch" >>. parenExp) body (fun e cl -> Ast.Switch(e, cl))

let mutable private forbiddenNames = []

let macro =
Expand Down Expand Up @@ -316,6 +327,7 @@ module private ParseImpl =
ifStatement
whileLoop
doWhileLoop
switch
verbatim |>> Ast.Verbatim
macro |>> Ast.Verbatim
attribute |>> Ast.Verbatim
Expand Down
13 changes: 11 additions & 2 deletions src/printer.fs
Original file line number Diff line number Diff line change
Expand Up @@ -121,9 +121,9 @@ module private PrinterImpl =

let backslashN() =
match outputFormat with
| Options.Text | Options.JS -> "\n"
| Options.Text | Options.JS | Options.IndentedText -> "\n"
| Options.Nasm -> "', 10, '"
| _ -> "\\n"
| Options.CHeader | Options.CList -> "\\n"

// Print HLSL semantics
let semToS sem =
Expand Down Expand Up @@ -215,6 +215,15 @@ module private PrinterImpl =
let s = if System.Char.IsLetterOrDigit s.[s.Length - 1] then s + " " else s
if s <> "" && s.[0] = '#' then out "%s%s" (backslashN()) (escape s)
else escape s
| Switch(e, cl) ->
let labelToS = function
| Case e -> out "case %s:" (exprToS e)
| Default -> out "default:"
let caseToS (l, sl) =
let stmts = List.map (stmtToS (indent+2)) sl |> String.concat ""
out "%s%s%s" (nl (indent+1)) (labelToS l) stmts
let body = List.map caseToS cl |> String.concat ""
out "%s(%s){%s%s}" "switch" (exprToS e) body (nl indent)

and stmtToS indent i =
out "%s%s" (nl indent) (stmtToS' indent i)
Expand Down
11 changes: 11 additions & 0 deletions src/renamer.fs
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,17 @@ module private RenamerImpl =
env
| Jump(_, e) -> renOpt e; env
| Verbatim _ -> env
| Switch(e, cl) ->
let renLabel = function
| Case e -> renExpr env e
| Default -> ()
let renCase env (l, sl) =
renLabel l
renList env renStmt sl |> ignore<Env>
env
renExpr env e
renList env renCase cl |> ignore<Env>
env

let rec renTopLevelName env = function
| TLDecl d -> renDecl true env d
Expand Down
2 changes: 1 addition & 1 deletion src/rewriter.fs
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ let findInlinable block =
localDefs.[def.name.Name] <- (def.name, deps.Count > 0)
| Expr e
| Jump (_, Some e) -> localExpr <- e :: localExpr
| Verbatim _ | Jump (_, None) | Block _ | If _| ForE _ | ForD _ | While _ | DoWhile _ -> ()
| Verbatim _ | Jump (_, None) | Block _ | If _| ForE _ | ForD _ | While _ | DoWhile _ | Switch _ -> ()

let localReferences = collectReferences (List.map Expr localExpr)
let allReferences = collectReferences block
Expand Down
1 change: 1 addition & 0 deletions tests/commands.txt
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
-o tests/unit/function_overload.expected tests/unit/function_overload.frag
-o tests/unit/externals.expected tests/unit/externals.frag
-o tests/unit/macros.expected --no-inlining tests/unit/macros.frag
--format indented -o tests/unit/switch.expected tests/unit/switch.frag

# Optimization tests

Expand Down
45 changes: 45 additions & 0 deletions tests/unit/switch.expected
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
void h()
{
switch(42){
case 42:
break;
}
}

#define GOOD 42

void i()
{
switch(42){
case GOOD:
break;
}
}
void h(in int i)
{
switch(i){
case 0:
break;
}
}
void i(in int i)
{
switch(i+42){
case 0:
break;
}
}
float G()
{
switch(42){
case 42:
float i=4.2;
float k=2.4;
return i+k;
case 43:
float i=4.3;
return i+3.4;
default:
return 0.;
}
}
42 changes: 42 additions & 0 deletions tests/unit/switch.frag
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
void switchConst() {
switch (42) {
case 42:
break;
}
}

#define GOOD 42
void switchDefine() {
switch (42) {
case GOOD:
break;
}
}

void switchArg(in int someArg) {
switch (someArg) {
case 0:
break;
}
}

void switchExpr(in int someArg) {
switch (someArg + 42) {
case 0:
break;
}
}

float switchMultiple() {
switch (42) {
case 42:
float someVar = 4.2;
float otherVar = 2.4;
return someVar + otherVar;
case 43:
float someVar = 4.3;
return someVar + 3.4;
default:
return 0.0;
}
}

0 comments on commit 342adde

Please sign in to comment.