Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Regex support in valid key #200

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open

Regex support in valid key #200

wants to merge 2 commits into from

Conversation

jocelynke
Copy link

Working for python, cpp_stl_11. Have to be tested for other implementations (C#, Go, Java, JS, Ruby, Rust, PHP, Perl), not implemented on remaining. Can't work on cpp_stl_98 without other library.

Working for python, cpp_stl_11. Have to be tested for other implementations (C#, Go, Java, JS, Ruby, Rust, PHP, Perl), not implemented on remaining. Can't work on cpp_stl_98 without other library.
@@ -253,6 +253,11 @@ class GoTranslator(out: StringLanguageOutputWriter, provider: TypeProvider, impo
}
}

override def doRegexMatchOp(str: String, regex: String): String = {
importList.add("regexp")
s"matched, _ := regexp.MatchString(`${regex}`, ${trStringLiteral(str)}); matched"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will this fail if it will be used twice in the same function?

Copy link
Member

@generalmimon generalmimon Apr 3, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And I think it's not wise to ignore the error in case it occurs (doing matched, _ :=). Please follow the same approach used in the rest of the GoTranslator, notice that the methods you added are ones of the few in the file returning String and not TranslatorResult. There is as special method outVarCheckRes handling the errors returned from functions, I suggest using that one.

Copy link
Author

@jocelynke jocelynke Apr 4, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to modify the translateExpr func in GoTranslator, but it seems to me that this function is not executed (I use some logs), could you explain it to me ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will this fail if it will be used twice in the same function?

Yes, it probably will, because the variables can't be redeclared in the same scope in Go unless some new variables are introduced. I've made a simple test: https://repl.it/repls/WonderfulJointProcedure

I tried to modify the translateExpr func in GoTranslator, but it seems to me that this function is not executed

@jocelynke Go language didn't play well with the classic BaseTranslator model that returns Strings, because of the error handling in Go. Go doesn't have try/catch as other languages, it treats errors as values. This means that if you want the error from a function you call to bubble up in the call stack, it won't happen automatically. You have to check if the error value returned from the function is nil and if it's not, then an error occured and you should return it to the caller. (Just returning it isn't exactly debug-friendly, because the caller loses any context which function in the stack actually caused the error, but it's unfortunately done this way in KS currently.)

This means that parsing a single u1 field from the stream can't be done in a single expression (because it can of course result in an EOF error), but instead it is a 3-phase process:

func (this *HelloWorld) Read(...) (err error) {
 // ...
    tmp1, err := this._io.ReadU1()
    if err != nil {
        return err
    }
    this.One = tmp1

Because of this, GoTranslator can't just extend BaseTranslator as other languages do, because the methods in BaseTranslator have return type String which doesn't fit for Go (and it can't be changed in the derived classes). Not all methods in GoTranslator can be implemented in a single expression which can be passed as String, for these the return value of the called error-returning method is saved to a temporary variable (named tmp1, tmp2, ... to avoid name conflicts), the error is handled and only the "serial number" of the temp variable is returned. This is done by the method outVarCheckRes.

Hence most methods in GoTranslator return TranslatorResult, which is defined on the top:

sealed trait TranslatorResult
case class ResultString(s: String) extends TranslatorResult
case class ResultLocalVar(n: Int) extends TranslatorResult

And now how you should do it. I think the doRegexMatchOp here could look like this:

override def doRegexMatchOp(str: String, regex: String): TranslatorResult = {
  importList.add("regexp")
  outVarCheckRes(s"regexp.MatchString(`${regex}`, ${trStringLiteral(str)})")

Then you'll have to move the abstract definition of this method from CommonOps to BaseTranslator:

def doRegexMatchOp(str: String, regex: String): String

The reason is that CommonOps is inherited by GoTranslator too (whereas BaseTranslator is not), but in GoTranslator you need to specify a different return type, which won't work in Scala. And the Ast.expr.RegexMatch handling in BaseTranslator

case Ast.expr.RegexMatch(str: Ast.expr, regex: String) => {
detectType(str) match {
case (_: StrType) =>
doRegexMatchOp(translate(str), doRegex(regex))
case _ =>
throw new TypeMismatchError(s"regex match need strings")
}
}

needs to be in GoTranslator as well, because again, it doesn't extend BaseTranslator.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for this deep explanation, I had implemented what you told me, but the thing is I have no log of a call to translateExpr in the GoTranslator. The consequence is that no code is generated to validate the field that needs it.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@generalmimon I found that the valid key is not supported by Go in kaitai, so it's normal that no code is generated...

@generalmimon
Copy link
Member

@jocelynke Could you please push your changes to your branch jocelynke:regex_support so we can see the changes you've made? It seems that you're just marking the things as resolved, but you don't publish anything so we can't say. The commits you push into this branch will appear in this PR.

@jocelynke
Copy link
Author

@generalmimon I wanted to correct everything before pushing. Here are the remaining to do : Go support, Rust dynamic include

* Error to be thrown when validation fails with actual not matching regex
* @param _dt data type used in validation process
*/
case class ValidationRegexMatchError(_dt: DataType) extends ValidationError(_dt) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For equality checks (and storing original value in question for C++), we need to know original value data type. But regular expression matches would likely only work for strings. What's the point in passing _dt: DataType here?

}
}


Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: unnecessary double newline.

Suggested change


def fromYaml(src: Any, path: List[String]): ValidationSpec = {
src match {
case value: String =>

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: unnecessary newline

Suggested change

@@ -198,4 +206,6 @@ abstract class BaseTranslator(val provider: TypeProvider)
// for the language
def anyField(value: Ast.expr, attrName: String): String =
s"${translate(value)}.${doName(attrName)}"

def doRegexMatchOp(str: String, regex: String): String
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: formatting.

Suggested change
def doRegexMatchOp(str: String, regex: String): String
def doRegexMatchOp(str: String, regex: String): String

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants