"Assignments are not expressions error" after converting Java to Kotlin - kotlin

I converted some Java classes to kotlin and an "Assignments are not expressions, and only expressions are allowed in this context" error pops up when I try to run this code which worked fine in Java:
#Throws(IOException::class)
private fun readAll(rd: Reader): String {
val sb = StringBuilder()
var cp: Int
while ((cp = rd.read()) != -1) {
sb.append(cp.toChar())
}
return sb.toString()
}
The line causing the problem:
while ((cp = rd.read()) != -1)

Exactly as the message says in Kotlin you can't use the assignment as an expression.
You can do this:
private fun readAll(rd: Reader): String {
val sb = StringBuilder()
var cp: Int
do {
cp = rd.read()
if (cp == -1)
break
sb.append(cp.toChar())
} while (true) // your choice here to stop the loop
return sb.toString()
}

In Kotlin you can't do this:
while ((cp = rd.read()) != -1)
You should use something like this:
var cp = rd.read()
while (cp != -1) {
// your logic here
cp = rd.read()
}
Or something like this:
while (true) {
val cp = rd.read()
if (cp < 0) break
// your logic here
}
Because assignment (cp = rd.read()) is expression in Java, but not in Kotlin.

Related

Syntax Highlighting for go in vb.net

Ok so I have been making a simple code editor in vb.net for go.. (for personal uses)
I tried this code -
Dim tokens As String = "(break|default|func|interface|select|case|defer|go|map|struct|chan|else|goto|package|switch|const|fallthrough|if|range|type|continue|for|import|return|var)"
Dim rex As New Regex(tokens)
Dim mc As MatchCollection = rex.Matches(TextBox2.Text)
Dim StartCursorPosition As Integer = TextBox2.SelectionStart
For Each m As Match In mc
Dim startIndex As Integer = m.Index
Dim StopIndex As Integer = m.Length
TextBox2.[Select](startIndex, StopIndex)
TextBox2.SelectionColor = Color.FromArgb(0, 122, 204)
TextBox2.SelectionStart = StartCursorPosition
TextBox2.SelectionColor = Color.RebeccaPurple
Next
but I couldn't add something like print statements say I want a fmt.Println("Hello World"), that is not possible, anyone help me?
I want a simple result that will do proper syntax without glitching text colors like this current code does.
Here's a code showing how to update highlighting with strings and numbers.
You would need to tweak it further to support syntax like comments, etc.
private Regex BuildExpression()
{
string[] exprs = {
"(break|default|func|interface|select|case|defer|go|map|struct|chan|else|goto|package|switch|const|fallthrough|if|range|type|continue|for|import|return|var)",
#"([0-9]+\.[0-9]*(e|E)(\+|\-)?[0-9]+)|([0-9]+\.[0-9]*)|([0-9]+)",
"(\"\")|\"((((\\\\\")|(\"\")|[^\"])*\")|(((\\\\\")|(\"\")|[^\"])*))"
};
StringBuilder sb = new StringBuilder();
for (int i = 0; i < exprs.Length; i++)
{
string expr = exprs[i];
if ((expr != null) && (expr != string.Empty))
sb.Append(string.Format("(?<{0}>{1})", "_" + i.ToString(), expr) + "|");
}
if (sb.Length > 0)
sb.Remove(sb.Length - 1, 1);
RegexOptions options = RegexOptions.ExplicitCapture | RegexOptions.IgnorePatternWhitespace | RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.IgnoreCase;
return new Regex(sb.ToString(), options);
}
private void HighlightSyntax()
{
var colors = new Dictionary<int, Color>();
var expression = BuildExpression();
Color[] clrs = { Color.Teal, Color.Red, Color.Blue };
int[] intarray = expression.GetGroupNumbers();
foreach (int i in intarray)
{
var name = expression.GroupNameFromNumber(i);
if ((name != null) && (name.Length > 0) && (name[0] == '_'))
{
var idx = int.Parse(name.Substring(1));
if (idx < clrs.Length)
colors.Add(i, clrs[idx]);
}
}
foreach (Match match in expression.Matches(richTextBox1.Text))
{
int index = match.Index;
int length = match.Length;
richTextBox1.Select(index, length);
for (int i = 0; i < match.Groups.Count; i++)
{
if (match.Groups[i].Success)
{
if (colors.ContainsKey(i))
{
richTextBox1.SelectionColor = colors[i];
break;
}
}
}
}
}
What we found during development of our Code Editor libraries, is that the regular expression-based parsers are hard to adapt to fully support advanced syntax like contextual keywords (LINQ) or interpolated strings.
You might find a bit more information here:
https://www.alternetsoft.com/blog/code-parsing-explained
The most accurate syntax highlighting for VB.NET can be implemented using Microsoft.CodeAnalysis API, it's the same API used internally by Visual Studio text editor.
Below is sample code showing how to get classified spans for VB.NET code (every span contains start/end position within the text and classification type, i.e. keyword, string, etc.). These spans then can be used to highlight text inside a textbox.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Classification;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Text;
public class VBClassifier
{
private Workspace workspace;
private static string FileContent = #"
Public Sub Run()
Dim test as TestClass = new TestClass()
End Sub";
public void Classify()
{
var project = InitProject();
var doc = AddDocument(project, "file1.vb", FileContent);
var spans = Classify(doc);
}
protected IEnumerable<ClassifiedSpan> Classify(Document document)
{
var text = document.GetTextAsync().Result;
var span = new TextSpan(0, text.Length);
return Classifier.GetClassifiedSpansAsync(document, span).Result;
}
protected Document AddDocument(Project project, string fileName, string code)
{
var documentId = DocumentId.CreateNewId(project.Id, fileName);
ApplySolutionChanges(s => s.AddDocument(documentId, fileName, code, filePath: fileName));
return workspace.CurrentSolution.GetDocument(documentId);
}
protected virtual void ApplySolutionChanges(Func<Solution, Solution> action)
{
var solution = workspace.CurrentSolution;
solution = action(solution);
workspace.TryApplyChanges(solution);
}
protected MefHostServices GetRoslynCompositionHost()
{
IEnumerable<Assembly> assemblies = MefHostServices.DefaultAssemblies;
var compositionHost = MefHostServices.Create(assemblies);
return compositionHost;
}
protected Project CreateDefaultProject()
{
var solution = workspace.CurrentSolution;
var projectId = ProjectId.CreateNewId();
var projectName = "VBTest";
ProjectInfo projectInfo = ProjectInfo.Create(
projectId,
VersionStamp.Default,
projectName,
projectName,
LanguageNames.VisualBasic,
filePath: null);
ApplySolutionChanges(s => s.AddProject(projectInfo));
return workspace.CurrentSolution.Projects.FirstOrDefault();
}
protected Project InitProject()
{
var host = GetRoslynCompositionHost();
workspace = new AdhocWorkspace(host);
return CreateDefaultProject();
}
}
Update:
Here's a Visual Studio project demonstrating both approaches:
https://drive.google.com/file/d/1LLuzy7yDFAE-v40I7EswECYQSthxheEf/view?usp=sharing

Is there any method in ByteBuddy to convert a TypeDescription.Generic into an appropriate java.lang.reflect.Type?

(The surface area of the ByteBuddy API is overwhelmingly enormous, which is why I'm asking the question.)
I'm aware that I can take a TypeDescription.Generic and determine its "sort" and proceed rather laboriously "by hand" from there, but often times I've found there is a method buried somewhere that will do this sort of tedious work for me.
EDIT: a commenter asked for the "tedious" recipe. Here it is (stand back; please note the various implementations of various Types are more or less what you'd expect them to be):
public static final Type toType(final TypeDefinition type) throws ReflectiveOperationException {
final Type returnValue;
if (type == null) {
returnValue = null;
} else {
final TypeDescription.Generic genericType = type.asGenericType();
switch (type.getSort()) {
case GENERIC_ARRAY:
returnValue = new DefaultGenericArrayType(toType(type.getComponentType()));
break;
case NON_GENERIC:
returnValue = Class.forName(type.getTypeName(), false, Thread.currentThread().getContextClassLoader());
break;
case PARAMETERIZED:
final TypeDefinition ownerType = genericType.getOwnerType();
final TypeDefinition rawType = type.asErasure();
final List<? extends TypeDefinition> actualTypeArguments = genericType.getTypeArguments();
if (actualTypeArguments == null || actualTypeArguments.isEmpty()) {
returnValue = new DefaultParameterizedType(toType(ownerType), toType(rawType));
} else {
final Type[] actualJavaTypeArguments = new Type[actualTypeArguments.size()];
for (int i = 0; i < actualTypeArguments.size(); i++) {
actualJavaTypeArguments[i] = toType(actualTypeArguments.get(i));
}
returnValue = new DefaultParameterizedType(toType(ownerType), toType(rawType), actualJavaTypeArguments);
}
break;
case VARIABLE:
final TypeVariableSource typeVariableSource = genericType.getTypeVariableSource();
final GenericDeclaration gd;
if (typeVariableSource instanceof TypeDefinition typeDefinition) {
gd = Class.forName(typeDefinition.asErasure().getTypeName(), false, Thread.currentThread().getContextClassLoader());
} else if (typeVariableSource instanceof MethodDescription.InDefinedShape methodDescription) {
// Reflection time
final String name = methodDescription.getName();
final Class<?> cls = Class.forName(methodDescription.getDeclaringType().asErasure().getTypeName(), false, Thread.currentThread().getContextClassLoader());
final List<? extends TypeDefinition> parameterTypes = methodDescription.getParameters().asTypeList();
final Class<?>[] parameterClasses = new Class<?>[parameterTypes.size()];
for (int i = 0; i < parameterTypes.size(); i++) {
parameterClasses[i] = Class.forName(parameterTypes.get(i).asErasure().getName(), false, Thread.currentThread().getContextClassLoader());
}
if (MethodDescription.CONSTRUCTOR_INTERNAL_NAME.equals(name)) {
assert TypeDescription.VOID.equals(methodDescription.getReturnType());
gd = cls.getDeclaredConstructor(parameterClasses);
} else {
assert !MethodDescription.TYPE_INITIALIZER_INTERNAL_NAME.equals(name);
gd = cls.getDeclaredMethod(name, parameterClasses);
}
} else {
throw new IllegalArgumentException("Unexpected type variable source: " + typeVariableSource);
}
final TypeVariable<?>[] typeVariables = gd.getTypeParameters();
TypeVariable<?> temp = null;
for (final TypeVariable<?> typeVariable : typeVariables) {
if (typeVariable.getName().equals(genericType.getSymbol())) {
temp = typeVariable;
break;
}
}
assert temp != null;
returnValue = temp;
break;
case VARIABLE_SYMBOLIC:
throw new IllegalArgumentException("Unexpected type: " + type);
case WILDCARD:
final List<? extends TypeDefinition> upperBounds = genericType.getUpperBounds();
final List<? extends TypeDefinition> lowerBounds = genericType.getLowerBounds();
if (lowerBounds == null || lowerBounds.isEmpty()) {
if (upperBounds == null || upperBounds.isEmpty() || (upperBounds.size() == 1 && TypeDescription.Generic.OBJECT.equals(upperBounds.get(0)))) {
returnValue = UnboundedWildcardType.INSTANCE;
} else {
// Upper bounded.
final Type[] upperJavaBounds = new Type[upperBounds.size()];
for (int i = 0; i < upperBounds.size(); i++) {
upperJavaBounds[i] = toType(upperBounds.get(i)); // XXX recursive
}
returnValue = new UpperBoundedWildcardType(upperJavaBounds);
}
} else {
assert upperBounds == null || upperBounds.isEmpty() || (upperBounds.size() == 1 && TypeDescription.Generic.OBJECT.equals(upperBounds.get(0))) : "Unexpected upper bounds: " + upperBounds + "; lower bounds: " + lowerBounds;
// Lower bounded.
assert lowerBounds.size() == 1 : "Unexpected size in lower bounds: " + lowerBounds;
returnValue = new LowerBoundedWildcardType(toType(lowerBounds.get(0))); // XXX recursive
}
break;
default:
throw new IllegalArgumentException("Unexpected type: " + type);
}
}
return returnValue;
}
No, you can only convert a Type to a TypeDescription.Generic but there is no option to do it the other way. The easiest option to emulate this would probably be to define a class that defines a field of the given Type, to load this class and to read the field type using Java reflection.
The reason Byte Buddy cannot convert a description to a Type is that Byte Buddy abstracts out class loaders and that type variables might be detached from their declaring source.

Why cannot I get PsiClass from Kotlin but successful in Java

I try to get PsiClass from Kotlin file (the PsiFile instance is KtFile), but aways get null, In Java case is successful.
PsiFile psiFile = e.getData(CommonDataKeys.PSI_FILE);
Editor editor = e.getData(CommonDataKeys.EDITOR);
// null in kotlin
PsiClass psiClass = getPsiClass(editor, psiFile);
.....
private PsiClass getPsiClass(Editor editor, PsiFile file) {
int offset = editor.getCaretModel().getOffset();
PsiElement element = file.findElementAt(offset);
if (element == null)
element = file.findElementAt(offset - 1);
if (element == null) {
return null;
} else {
PsiClass target = (PsiClass) PsiTreeUtil.getParentOfType(element, PsiClass.class);
if (target == null) {
element = file.findElementAt(offset - 1);
if (element == null)
return null;
target = (PsiClass) PsiTreeUtil.getParentOfType(element, PsiClass.class);
}
return target instanceof SyntheticElement ? null : target;
}
}
PsiClass is used only for java classes. For Kotlin you should use KtClass.

Best way to translate this java code into kotlin

URL url = new URL(urlSpec);
HttpURLConnection connection = (HttpURLConnection)url.openConnection();
InputStream in = connection.getInputStream();
int bytesRead = 0;
byte[] buffer = new byte[1024];
while ((bytesRead = in.read(buffer)) > 0) {
out.write(buffer, 0, bytesRead);
}
out.close();
I am especially curious about this part
while(bytesRead = in.read(buffer))
We know that asigements are treated as statements in kotlin while in java they are treated as expressions, so this construct is only possible in java.
What is best way to translate this java code into kotlin?
Instead of translating the code literally, make use of Kotlin's stdlib which offers a number of useful extension functions. Here's one version
val text = URL(urlSpec).openConnection().inputStream.bufferedReader().use { it.readText() }
To answer the original question: You're right, assignments are not treated as expressions. Therefore you will need to separate the assignment and the comparison. Take a look at the implementation in the stdlib for an example:
public fun Reader.copyTo(out: Writer, bufferSize: Int = DEFAULT_BUFFER_SIZE): Long {
var charsCopied: Long = 0
val buffer = CharArray(bufferSize)
var chars = read(buffer)
while (chars >= 0) {
out.write(buffer, 0, chars)
charsCopied += chars
chars = read(buffer)
}
return charsCopied
}
Source: https://github.com/JetBrains/kotlin/blob/a66fc9043437d2e75f04feadcfc63c61b04bd196/libraries/stdlib/src/kotlin/io/ReadWrite.kt#L114
You could use apply block to execute the assignment:
val input= connection.getInputStream();
var bytesRead = 0;
val buffer = ByteArray(1024)
while (input.read(buffer).apply { bytesRead = this } > 0) {
out.write(buffer, 0, bytesRead);
}
You could use something like this
This operation may be little heavy as a function is created each iteration.
val url = URL("urlSpec")
val connection = url.openConnection() as HttpURLConnection
val `in` = connection.inputStream
val buffer = ByteArray(1024)
var bytesRead: Int? = null
while ({ bytesRead = `in`.read(buffer); bytesRead }() != null) {
out.write(buffer, 0, bytesRead!!)
}
out.close()

Mvel dynamic expression

Do you know if it's possible to dynamically evaluate an expression with Mvel. For example :
VariableResolverFactory functionFactory = new MapVariableResolverFactory();
MVEL.eval("def SUM(op1,op2,op3) { result=0B; if(op1) result+=op2; else result+=op3; } return result; ",functionFactory);
ParserContext ctx = new ParserContext()
Serializable s = MVEL.compileExpression("SUM(op1,op2,op3)", ctx);
contextMapFct.put("op1", "5 > 3"); // just as an example if it's useless
contextMapFct.put("op2", new BigDecimal(10));
contextMapFct.put("op3", new BigDecimal(30));
Object obj= MVEL.executeExpression(s, contextMapFct, this.functionFactory);
Few changes done
1.) Brace added at the last, was closing before return result;.
2.) int result=0, declaration added.
3.) if(op1 == 'true'), it is not coming as boolean, but as String
VariableResolverFactory functionFactory = new MapVariableResolverFactory();
MVEL.eval(
"def SUM(op1,op2,op3) { int result=0; if(op1 == 'true') result+=op2; else result+=op3; return result; }",
functionFactory);
ParserContext ctx = new ParserContext();
Serializable s = MVEL.compileExpression("SUM(op1,op2,op3)", ctx);
Map contextMapFct = new HashMap();
contextMapFct.put("op1", "5 > 3"); // just as an example if it's useless
contextMapFct.put("op2", new BigDecimal(10));
contextMapFct.put("op3", new BigDecimal(30));
Object obj = MVEL.executeExpression(s, contextMapFct, functionFactory);
System.out.println(obj);
output
30