diff --git a/app/elm/Compiler/Ast.elm b/app/elm/Compiler/Ast.elm index d8bbe38..e973ff3 100644 --- a/app/elm/Compiler/Ast.elm +++ b/app/elm/Compiler/Ast.elm @@ -66,6 +66,7 @@ type Node | Repeat Node (List Node) | Until Node (List Node) | Foreach Node (List Node) + | For String Node Node (List Node) | Map Node (List Node) | Filter Node (List Node) | If Node (List Node) @@ -161,6 +162,9 @@ typeOfCallee node = Foreach _ _ -> Command { name = "foreach" } + For _ _ _ _ -> + Command { name = "for" } + Map _ _ -> Primitive { name = "map" } @@ -606,6 +610,33 @@ compile context node = ] |> List.concat + For variable start end children -> + let + compiledStart = + compileInContext (Expression { caller = "for" }) start + + compiledEnd = + compileInContext (Expression { caller = "for" }) end + + compiledChildren = + List.concatMap (compileInContext Statement) children + in + [ compiledStart + , [ StoreVariable variable ] + , compiledChildren + , [ PushVariable variable ] + , compiledEnd + , [ Eval2 { name = "greaterp", f = P.greaterp } + , JumpIfFalse 6 + , PushVariable variable + , PushValue (Type.Int 1) + , Eval2 { name = "+", f = P.sum2 } + , StoreVariable variable + , Jump (List.length compiledChildren + List.length compiledEnd + 7 |> negate) + ] + ] + |> List.concat + Map iterator children -> compileMap iterator children diff --git a/app/elm/Compiler/Parser.elm b/app/elm/Compiler/Parser.elm index 7eedb16..afa0c6e 100644 --- a/app/elm/Compiler/Parser.elm +++ b/app/elm/Compiler/Parser.elm @@ -7,6 +7,7 @@ module Compiler.Parser exposing , defaultState , functionDefinition , output + , statement , term , withExistingFunctions ) @@ -279,6 +280,7 @@ statement state = [ inParentheses state , P.lazy (\_ -> ifElse state) , P.lazy (\_ -> foreach state) + , P.lazy (\_ -> for state) , P.lazy (\_ -> map state) , P.lazy (\_ -> filter state) , P.lazy (\_ -> repeat state) @@ -395,6 +397,25 @@ foreach state = P.lazy (\_ -> controlStructure state { keyword = "foreach", constructor = Ast.Foreach }) +for : State -> Parser Ast.Node +for state = + P.inContext (Keyword "for") <| + P.succeed Ast.For + |. Helper.keyword "for" + |. Helper.spaces + |. Helper.symbol "[" + |. Helper.maybeSpaces + |= Value.rawWordInList + |. Helper.spaces + |= booleanExpression state + |. Helper.spaces + |= booleanExpression state + |. Helper.maybeSpaces + |. Helper.symbol "]" + |. Helper.spaces + |= instructionList state + + map : State -> Parser Ast.Node map state = P.lazy (\_ -> invertedControlStructure state { keyword = "map", constructor = Ast.Map }) diff --git a/app/elm/Compiler/Parser/Value.elm b/app/elm/Compiler/Parser/Value.elm index 7f0d8e4..8a1d11d 100644 --- a/app/elm/Compiler/Parser/Value.elm +++ b/app/elm/Compiler/Parser/Value.elm @@ -1,6 +1,6 @@ module Compiler.Parser.Value exposing - ( value - , wordOutsideList + ( rawWordInList + , value ) {-| This module provides functions for parsing Logo values. @@ -22,7 +22,6 @@ import Parser.Advanced as P , map , oneOf , succeed - , symbol ) import Vm.Type as Type @@ -117,8 +116,8 @@ valueInList = ] -wordInList : Parser context Problem Type.Value -wordInList = +rawWordInList : Parser context Problem String +rawWordInList = let isWordCharacter : Char -> Bool isWordCharacter c = @@ -136,10 +135,14 @@ wordInList = && (c /= '<') && (c /= '>') in - ((succeed () + (succeed () |. chompIf isWordCharacter ExpectingStartOfWord |. chompWhile isWordCharacter - ) - |> getChompedString ) + |> getChompedString + + +wordInList : Parser context Problem Type.Value +wordInList = + rawWordInList |> map Type.Word diff --git a/tests/Test/Parser.elm b/tests/Test/Parser.elm index 7279be5..62a2144 100644 --- a/tests/Test/Parser.elm +++ b/tests/Test/Parser.elm @@ -208,3 +208,31 @@ booleanAlgebra = , parsesBooleanExpression "1 <> 1" , parsesBooleanExpression "2 * (3 + 1) = (4 + 4) * 7" ] + + +parsesStatement : String -> Test +parsesStatement statement = + let + match : Result (List (DeadEnd context problem)) Ast.Node -> Expectation + match result = + case result of + Ok _ -> + Expect.pass + + Err _ -> + Expect.fail <| "could not parse statement \"" ++ statement ++ "\"" + in + test statement <| + \_ -> + match + (Parser.run (Parser.statement defaultState) statement) + + +controlStructures : Test +controlStructures = + describe "control structures" <| + [ describe "for" <| + [ parsesStatement "for [ i 0 10 ] [ print :i ]" + , parsesStatement "for [i 0 10] [ print :i ]" + ] + ] diff --git a/tests/Test/Run.elm b/tests/Test/Run.elm index 2d324a9..7b6aa90 100644 --- a/tests/Test/Run.elm +++ b/tests/Test/Run.elm @@ -79,6 +79,14 @@ foreachWithTemplateVariable = ] +for : Test +for = + describe "for" <| + [ printsLines "for [ i 0 3 ] [ print :i ]" [ "0", "1", "2", "3" ] + , printsLines "make \"a 0 make \"b 3 for [ i :a :b ] [ print :i ]" [ "0", "1", "2", "3" ] + ] + + map : Test map = describe "map" <| diff --git a/tests/Test/Run/Builtin.elm b/tests/Test/Run/Builtin.elm index 2cf67f7..1d6600e 100644 --- a/tests/Test/Run/Builtin.elm +++ b/tests/Test/Run/Builtin.elm @@ -99,10 +99,12 @@ primitives = , describe "greaterp" <| [ printsLine "print greaterp 1 1" "false" , printsLine "print greater? 1 1" "false" + , printsLine "print greaterp 2 1" "true" ] , describe "lessp" <| [ printsLine "print lessp 1 1" "false" , printsLine "print less? 1 1" "false" + , printsLine "print lessp 1 2" "true" ] , describe "remainder" <| [ printsLine "print remainder 1 1" "0" ]