diff --git a/fcore/app-with.sml b/fcore/app-with.sml index 1412380..3c691e7 100644 --- a/fcore/app-with.sml +++ b/fcore/app-with.sml @@ -10,7 +10,7 @@ struct , buffer , bufferModifyTime , searchList - , searchString + , dfa , mode , windowWidth , windowHeight @@ -23,7 +23,7 @@ struct , buffer = buffer , bufferModifyTime = bufferModifyTime , searchList = searchList - , searchString = searchString + , dfa = dfa , mode = mode , windowWidth = windowWidth , windowHeight = windowHeight diff --git a/fcore/search-list/dfa-gen.sml b/fcore/search-list/dfa-gen.sml index 47838f2..6686bb7 100644 --- a/fcore/search-list/dfa-gen.sml +++ b/fcore/search-list/dfa-gen.sml @@ -15,6 +15,8 @@ sig val nextState: dfa * dfa_state * char -> dfa_state val isFinal: dfa * dfa_state -> bool val isDead: dfa_state -> bool + + val matchString: dfa * string -> (int * int) list end functor MakeDfaGen(Fn: DFA_GEN_PARAMS): DFA_GEN = @@ -817,6 +819,41 @@ struct end fun isDead (curState: dfa_state) = curState = ~1 + + fun helpMatchString (strPos, str, dfa, curState, startPos, prevFinalPos, acc) = + if strPos = String.size str then + let + val acc = + if prevFinalPos = ~1 then acc else (startPos, prevFinalPos) :: acc + in + List.rev acc + end + else + let + val chr = String.sub (str, strPos) + val newState = nextState (dfa, curState, chr) + val prevFinalPos = + if isFinal (dfa, newState) then strPos else prevFinalPos + in + if isDead newState then + if prevFinalPos = ~1 then + (* restart from startPos *) + helpMatchString (startPos + 1, str, dfa, 0, startPos + 1, ~1, acc) + else + let + val acc = (startPos, prevFinalPos) :: acc + in + helpMatchString + (prevFinalPos + 1, str, dfa, 0, prevFinalPos + 1, ~1, acc) + end + else + helpMatchString + (strPos + 1, str, dfa, newState, startPos, prevFinalPos, acc) + end + + fun matchString (dfa, string) = + if Vector.length dfa = 0 then [] + else helpMatchString (0, string, dfa, 0, 0, ~1, []) end structure CaseInsensitiveDfa = diff --git a/shf-tests.mlb b/shf-tests.mlb index 242d8fe..174a748 100644 --- a/shf-tests.mlb +++ b/shf-tests.mlb @@ -67,6 +67,7 @@ $(SML_LIB)/basis/mlton.mlb shell/exception-logger.sml test/Railroad/src/railroad.mlb +test/regex-tests.sml test/test-utils.sml test/normal-move.sml test/normal-delete.sml diff --git a/test/regex-tests.sml b/test/regex-tests.sml new file mode 100644 index 0000000..17d02d5 --- /dev/null +++ b/test/regex-tests.sml @@ -0,0 +1,43 @@ +structure RegexTests = +struct + open Railroad + open Railroad.Test + + structure CiDfa = CaseInsensitiveDfa + structure CsDfa = CaseSensitiveDfa + + val caseInsensitiveTests = describe "case insensitive regex" + [ test "recognises word 'hello' in string 'Hello world'" (fn _ => + let + (* arrange *) + val regexString = "hello" + val dfa = CiDfa.fromString regexString + val inputString = "Hello world" + + (* act *) + val matches = CiDfa.matchString (dfa, inputString) + + (* assert *) + val expectedMatches = [(0, 4)] + in + Expect.isTrue (matches = expectedMatches) + end) + , test "recognises word 'world' in string 'HELLO WORLD'" (fn _ => + let + (* arrange *) + val regexString = "world" + val dfa = CiDfa.fromString regexString + val inputString = "HELLO WORLD" + + (* act *) + val matches = CiDfa.matchString (dfa, inputString) + + (* assert *) + val expectedMatches = [(6, 10)] + in + Expect.isTrue (matches = expectedMatches) + end) + ] + + val tests = [caseInsensitiveTests] +end diff --git a/test/test.sml b/test/test.sml index e515900..416569a 100644 --- a/test/test.sml +++ b/test/test.sml @@ -6,7 +6,12 @@ struct fun main () = let val tests = - List.concat [NormalMove.tests, NormalDelete.tests, Regression.tests] + List.concat + [ NormalMove.tests + , NormalDelete.tests + , Regression.tests + , RegexTests.tests + ] val tests = concat tests in runWithConfig [Configuration.PrintPassed false] tests