diff --git a/src/ast.fs b/src/ast.fs index df2053c4..22e84a33 100644 --- a/src/ast.fs +++ b/src/ast.fs @@ -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*) @@ -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 diff --git a/src/parse.fs b/src/parse.fs index 6a2e4aed..5ae8b8aa 100644 --- a/src/parse.fs +++ b/src/parse.fs @@ -5,7 +5,6 @@ open Options.Globals module private ParseImpl = // TODO: true, false - // TODO: switch case open FParsec.Primitives open FParsec.CharParsers @@ -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 = @@ -316,6 +327,7 @@ module private ParseImpl = ifStatement whileLoop doWhileLoop + switch verbatim |>> Ast.Verbatim macro |>> Ast.Verbatim attribute |>> Ast.Verbatim diff --git a/src/printer.fs b/src/printer.fs index becb73a3..c315a0e1 100644 --- a/src/printer.fs +++ b/src/printer.fs @@ -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 = @@ -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) diff --git a/src/renamer.fs b/src/renamer.fs index b13cf57e..5edb23ad 100644 --- a/src/renamer.fs +++ b/src/renamer.fs @@ -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 + renExpr env e + renList env renCase cl |> ignore + env let rec renTopLevelName env = function | TLDecl d -> renDecl true env d diff --git a/src/rewriter.fs b/src/rewriter.fs index 37f7dd9b..bb79ab71 100644 --- a/src/rewriter.fs +++ b/src/rewriter.fs @@ -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 diff --git a/tests/commands.txt b/tests/commands.txt index 175de79d..c334bd61 100644 --- a/tests/commands.txt +++ b/tests/commands.txt @@ -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 diff --git a/tests/unit/switch.expected b/tests/unit/switch.expected new file mode 100644 index 00000000..ac2e4b2b --- /dev/null +++ b/tests/unit/switch.expected @@ -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.; + } +} diff --git a/tests/unit/switch.frag b/tests/unit/switch.frag new file mode 100644 index 00000000..bae0a7bf --- /dev/null +++ b/tests/unit/switch.frag @@ -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; + } +}