Suppose that we have two languages, A and B.

In language A, there are a lot of data-type declarations.

In language B there are no data-type declarations to the left of the assignment operator, but constructor calls do exist.

How would you recommend converting the type declarations into constructor calls?

Input Output
int x = lstring.numel() x = int(lstring.numel())
string final_msg = concat(err_msg, trace_str) final_msg = string(concat(err_msg, trace_str))

That is, the data type, such as int or string, has to be moved from the left-hand side of = over to the right-hand side and we have to insert some parentheses.

  • 1
    $\begingroup$ What sort of recommendation are you looking for here? Some sort of syntax rewriting system? sed -e 's/^\([^ ]*\) \([^=]*\)=\(\s*\)\(.*\)$/\2= \1(\4)/'? A language-level feature in A or B? $\endgroup$
    – Michael Homer
    Oct 22, 2023 at 2:19
  • 1
    $\begingroup$ Your question doesn't make sense, the input and output are doing fundamentally different things. On the left you have a compiler checking that your assignment is valid, based on additional metadata of the expected type; it will compile because lstring.numel() returns int-compatible (assuming it does). On the right, under the same assumption, x = lstring.numel() is already assigning an integer to x, so wrapping it in an int call isn't needed. Likewise if concat returns a string. (Given it looks like Python, the actual equivalent is e.g. x: int = lstring.numel().) $\endgroup$
    – jonrsharpe
    Oct 22, 2023 at 10:24
  • $\begingroup$ In other words, the difference between statically- and dynamically-typed languages is larger than just moving a word around and adding some parentheses. $\endgroup$
    – jonrsharpe
    Oct 22, 2023 at 10:29
  • 1
    $\begingroup$ This question isn't entirely non-sensical, because statically-typed languages do not only check the type of the right-hand-side; they also coerce it, or otherwise can have specific rules for literals in certain contexts. For example, double x = 5; coerces 5 to 5.0 in Java, and byte x = 5; contextually treats 5 as a byte literal rather than an int literal (this is different to coercion, and only applies to literals). When translating to another language, therefore, you might need to call a function to perform the conversion which is contextually implied in Java. $\endgroup$
    – kaya3
    Oct 22, 2023 at 13:03
  • 1
    $\begingroup$ That said, this is still a strange question because you are asking about translating only this, as if there aren't other differences between the two languages. In reality when transpiling from one language to another you most likely need an intermediate representation, which could be an AST including nodes for "invisible" operations such as coercions, so that explicit code can be emitted for those operations where required in the target language. So the answer depends heavily on your IR, but the question gives no details about it. $\endgroup$
    – kaya3
    Oct 22, 2023 at 13:09

1 Answer 1


For the examples provided, detecting declarations with regular expressions is possible, and creating their replacements by substitution. sed is made for that and

sed -e 's/^\([^ ]*\)\s*\([^ =][^=]*\)=\(\s*\)\(.*\)$/\2=\3\1(\4)/' code.langA

will find any complete single line with the form <single-word> <anything-but-=> = <anything-at-all> (matched using a standard POSIX basic regular expression) and shift the first word across, parenthesising whatever is on the right-hand side too. Lines that don't have that format will be unchanged.

sed ("stream editor") is a standard Unix command available on all Unix-likes, including macOS, and via WSL or Cygwin on Windows. Other tools and editors with regular-expression-based find-and-replace will be able to perform similar transformations too, likely with slightly different expression syntax.

While standard sed doesn't include it, many common versions have a -i mode for in-place updating of files. With those implementations,

sed -i.bak 's/^\([^ ]*\)\s*\([^ =][^=]*\)=\(\s*\)\(.*\)$/\2=\3\1(\4)/' *.langA

will fix every langA file at once where they are. If you have many files this will be more convenient. For more complex scenarios, you can ask on Unix & Linux for further guidance.

In general, a regular expression is not able to match all valid cases (or not able to avoid matching some invalid cases) in a typical programming language syntax. Language A may not have any of those cases, or they may just not come up in the code you're operating on, and then there's no issue. Many real-world codebases will be in this safe space in practice.

For maximum reliability, or for dealing with unknown or inconsistent code, you'd need a real parser specifically for language A. That parser would need to preserve enough source information to reconstruct the input (e.g. the kind of parser & syntax tree used for pretty-printing), and then it could be used to reproduce the input exactly with that small transformation made.

This will be more correct but may be significantly more work, and potentially not worthwhile if this is a one-off translation where errors will be easily detected for manual correction. For a repeated, automated, fragile, or unattended process, it will be essential. A real-world instance of a similar system is Python's 2to3 migration tool, which made various minor rewrites on top of a full Python 2 parser.


Not the answer you're looking for? Browse other questions tagged .