Skip to content

Commit bd5ae83

Browse files
committed
Bugfixes and updates, ready for ea+12
1 parent ff58f9d commit bd5ae83

File tree

12 files changed

+162
-24
lines changed

12 files changed

+162
-24
lines changed

CHANGELOG.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,30 @@
11
# Changelog
22

3+
### 3.0 Early Access Preview 12 (21 December 2021)
4+
5+
Implemented:
6+
- Support for various additional built-in functions, such as, e.g. `playTone`;
7+
- Runs existing TigerJython programs based on `gturtle`, etc.;
8+
- Updated version of Jython core (external GitHub repo) - includes support for `color`
9+
as a 'native' data type in TigerJython;
10+
11+
Bug fixes:
12+
- Syntax highlighting was erroneous, in part due to unexpected behaviour from the
13+
`RichTextFX` component used for the editor;
14+
15+
Known issues:
16+
- Syntax highlighting colour schemes are tentative and only available in selected themes;
17+
- Does not work with ARM architectures because of a bug in `sbt`/`maven` that prevents us
18+
from updating JavaFX to the required version 17;
19+
- Java emits a warning because we package JavaFX with out application (rather than
20+
providing external modules):
21+
`WARNING: Unsupported JavaFX configuration: classes were loaded from`. This can be
22+
ignored;
23+
24+
### 3.0 Early Access Previews 9-11
25+
26+
*A series of minor releases for internal testing with various bug fixes.*
27+
328
### 3.0 Early Access Preview 8 (2 August 2021)
429

530
Implemented:

README.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ the [JAR-file](https://github.com/Tiger-Jython/TigerJython/releases/download/v3.
77

88
> This is an early access version that is not yet ready for classroom use!!!
99
10-
**This version of TigerJython requires JRE version 9+ and will not run with JRE 8.**
10+
**This version of TigerJython requires JRE version 11+ and will not run with JRE 8.**
11+
One of the main reasons is that the JavaFX libraries used here requires an up-to-date
12+
Java version.
1113

1214

1315
## Features and Design
@@ -63,6 +65,19 @@ TigerJython uses [`sbt`](https://www.scala-sbt.org/) to build the code. Start `
6365
`compile` to compile the code and `assembly` to generate a JAR file containing all the necessary
6466
libraries (such as Scala, Jython, etc).
6567

68+
Note that this project depends on other sub-projects, which are included using symbolic links. If you
69+
are running Windows, you might have to manually replace the links in the `resources` directory with the
70+
text files, etc. used for display. There are three major sub-projects:
71+
- [TigerPython-Parser](https://github.com/Tobias-Kohn/TigerPython-Parser/)
72+
- [TigerJython-Localisation](https://github.com/Tiger-Jython/TigerJython-Localisation)
73+
- [TigerJython:Jython](https://github.com/Tiger-Jython/jython)
74+
75+
Additionally, TigerJython also includes various libraries such as extended turtle graphics, etc. You
76+
can find [some of these libraries on GitHub](https://github.com/Tiger-Jython/Aplu-Libraries),
77+
but not all of them are open-sourced and generally available. Please contact the authors if you require
78+
a copy of the full TigerJython support libraries (note: the editor and Jython will run perfectly fine
79+
without these, but it will not provide the full functionality of TigerJython).
80+
6681
By placing additional JARs into the `lib` subfolder, you can have additional files integrated into the
6782
project.
6883

build.sbt

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,8 @@ osName := (System.getProperty("os.name") match {
7070
})
7171

7272
// We need the newest version as it contains important fixes for Mac OS X
73-
val fxVersion = "14.0.1" // "11-ea+25"
73+
// Actually, version 17.0.1 is out but due to a bug in JavaFX maven the current version of SBT cannot pull it :-(
74+
val fxVersion = "16" // "11-ea+25"
7475

7576
/*libraryDependencies += "org.openjfx" % "javafx-base" % fxVersion classifier osName.value
7677
libraryDependencies += "org.openjfx" % "javafx-controls" % fxVersion classifier osName.value
@@ -94,6 +95,13 @@ libraryDependencies += "org.openjfx" % "javafx-controls" % fxVersion classifier
9495
libraryDependencies += "org.openjfx" % "javafx-fxml" % fxVersion classifier "mac"
9596
libraryDependencies += "org.openjfx" % "javafx-graphics" % fxVersion classifier "mac"
9697

98+
// In order to include OpenJFX for ARM architecture, we need at least version 17. However, due to a bug it is currently
99+
// not possible. We have to wait for updates to SBT/Maven before being able to pull the correct versions.
100+
/* libraryDependencies += "org.openjfx" % "javafx-base" % fxVersion classifier "linux-aarch64"
101+
libraryDependencies += "org.openjfx" % "javafx-controls" % fxVersion classifier "linux-aarch64"
102+
libraryDependencies += "org.openjfx" % "javafx-fxml" % fxVersion classifier "linux-aarch64"
103+
libraryDependencies += "org.openjfx" % "javafx-graphics" % fxVersion classifier "linux-aarch64" */
104+
97105
// Other dependencies
98106
libraryDependencies += "org.fxmisc.richtext" % "richtextfx" % "0.10.5"
99107

src/main/scala/tigerjython/syntaxsupport/SyntaxDocument.scala

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,27 +67,27 @@ class SyntaxDocument {
6767
val (pos, index) = tokenIndexFromPosition(position)
6868
// Check for the special case where we are deleting whitespace, which requires no re-parsing
6969
tokens(index) match {
70-
case whitespaceToken: WhitespaceToken if position + delLength <= pos + whitespaceToken.length =>
70+
case whitespaceToken: WhitespaceToken if position + delLength <= pos + whitespaceToken.length && delLength < whitespaceToken.length =>
7171
whitespaceToken.length -= delLength
7272
text.delete(position, position + delLength)
7373
return
7474
case Token(TokenType.NEWLINE, len) if position == pos + len && index + 1 < tokens.length =>
7575
tokens(index + 1) match {
7676
case whitespaceToken: WhitespaceToken =>
77-
if (whitespaceToken.length == delLength) {
77+
/*if (whitespaceToken.length == delLength) {
7878
tokens.remove(index + 1)
7979
text.delete(position, position + delLength)
8080
return
8181
}
82-
else if (whitespaceToken.length > delLength) {
82+
else*/ if (whitespaceToken.length > delLength) {
8383
tokens(index + 1).length -= delLength
8484
text.delete(position, position + delLength)
8585
return
8686
}
8787
case _ =>
8888
}
8989
// Do not reparse names if we merely remove a single character
90-
case token @ NameToken(TokenType.NAME, nameTokenType, tokenText) if pos < position &&
90+
case token @ NameToken(TokenType.NAME, _, tokenText) if pos < position &&
9191
position + delLength < pos + tokenText.length =>
9292
val p = position - pos
9393
val s = new StringBuilder(tokenText).delete(p, p + delLength).toString()
@@ -165,6 +165,12 @@ class SyntaxDocument {
165165
} else
166166
-1
167167

168+
/**
169+
* The `RichTextFX` component we use inserts control characters into the text before acting upon them. Hence, when
170+
* pressing the `delete` key, it first inserts `0x7F` and then deletes two characters. These control characters
171+
* upset the `SyntaxDocument` here. Rather than ignoring these control characters while parsing the text, we simply
172+
* throw away the messages of inserting them into our text in the first place.
173+
*/
168174
@inline
169175
private def _ignoreChar(c: Char): Boolean =
170176
if (c < ' ')
@@ -221,6 +227,10 @@ class SyntaxDocument {
221227
}
222228
case _ =>
223229
}
230+
// Check if we are inserting a new line at the end of an existing line
231+
if (insText == "\n" && tokens(index).tokenType == TokenType.NEWLINE) {
232+
// ToDo
233+
}
224234

225235
val length =
226236
if (position == pos + tokens(index).length && index + 1 < tokens.length)
@@ -268,8 +278,10 @@ class SyntaxDocument {
268278
tokenizer.extendParseRange(l)
269279
}
270280
}
271-
for (message <- tokens.createMessages())
281+
for (message <- tokens.createMessages()) {
272282
struct.handleMessage(message.index, message)
283+
}
284+
StructLine.validateAstNodes()
273285
}
274286
}
275287

src/main/scala/tigerjython/syntaxsupport/parser/PythonStmtParser.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import tigerjython.syntaxsupport.tokens._
1616
*/
1717
class PythonStmtParser(val document: SyntaxDocument) extends StmtParser {
1818

19-
private var _lambdaNames = collection.mutable.ArrayBuffer[String]()
19+
private val _lambdaNames = collection.mutable.ArrayBuffer[String]()
2020
private var _nameType: NameTokenType.Value = NameTokenType.UNKNOWN
2121
private var _source: TokenSource = _
2222

@@ -25,6 +25,7 @@ class PythonStmtParser(val document: SyntaxDocument) extends StmtParser {
2525
def parse(source: TokenSource): StatementType =
2626
if (source != null) {
2727
_source = source
28+
//source.dump()
2829
parseStmt()
2930
} else
3031
null

src/main/scala/tigerjython/syntaxsupport/parser/TokenSource.scala

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
*/
88
package tigerjython.syntaxsupport.parser
99

10-
import tigerjython.syntaxsupport.struct.StructElement
10+
import tigerjython.syntaxsupport.struct.{StructElement, StructLine}
1111
import tigerjython.syntaxsupport.tokens._
1212

1313
import scala.collection.BufferedIterator
@@ -19,20 +19,35 @@ class TokenSource(val source: TokenArray, val structElement: StructElement) exte
1919

2020
protected val tokens: Array[Token] =
2121
if (structElement != null && structElement.length > 0) {
22-
val idx = structElement.index
22+
val idx = structElement.index max 0
2323
val result = collection.mutable.ArrayBuffer[Token]()
2424
for (i <- idx until ((idx + structElement.length) min source.length)) {
2525
val tkn = source(i)
26-
if (tkn != null && tkn.tokenType != TokenType.WHITESPACE && tkn.tokenType != TokenType.COMMENT &&
27-
tkn.tokenType != TokenType.NEWLINE)
28-
result += tkn
26+
if (tkn != null) {
27+
if (tkn.tokenType == TokenType.NEWLINE) {
28+
if (structElement.isInstanceOf[StructLine])
29+
result += tkn
30+
}
31+
else if (tkn.tokenType != TokenType.WHITESPACE && tkn.tokenType != TokenType.COMMENT)
32+
result += tkn
33+
}
2934
}
3035
for (tkn <- result)
3136
tkn.annotation = false
3237
result.toArray
3338
} else
3439
Array()
3540

41+
def dump(): Unit = {
42+
println("TokenSource:")
43+
for ((token, i) <- tokens.zipWithIndex) {
44+
if (i == _index)
45+
print('|')
46+
print(token)
47+
}
48+
println()
49+
}
50+
3651
private var _index: Int = 0
3752

3853
def back(): Unit =

src/main/scala/tigerjython/syntaxsupport/struct/StructContainer.scala

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ abstract class StructContainer extends StructElement {
121121
}
122122

123123
def handleMessage(relIndex: Int, message: TokenChangeMessage): Unit =
124-
if (0 <= relIndex && relIndex <= length) {
124+
if (0 <= relIndex + offset && relIndex <= length) {
125125
passMessageToChildren(relIndex, message)
126126
if (!message.isHandled)
127127
message match {
@@ -148,9 +148,20 @@ abstract class StructContainer extends StructElement {
148148
invalidate(tokens)
149149
case _ =>
150150
}
151-
else if (isDefinitionStatement)
152-
invalidate(message.tokens)
153-
}
151+
else
152+
message match {
153+
case TokenChangeMessage.TokensDeleted(tokens, _, delCount) if relIndex + delCount < length =>
154+
if (relIndex < 0)
155+
length -= (relIndex + delCount)
156+
else
157+
length -= delCount
158+
invalidate(tokens)
159+
case TokenChangeMessage.TokensInserted(tokens, _, insCount) if relIndex < length =>
160+
length += insCount
161+
invalidate(tokens)
162+
case _ =>
163+
}
164+
}
154165

155166
protected def handleDeleteMessage(relIndex: Int, tokens: TokenArray, index: Int, delCount: Int): Boolean =
156167
if (children.nonEmpty)
@@ -175,8 +186,10 @@ abstract class StructContainer extends StructElement {
175186
case _ =>
176187
parse(tokens, index)
177188
}
178-
} else
189+
} else {
179190
parse(tokens, index)
191+
length -= delCount
192+
}
180193
true
181194
case _ =>
182195
length -= delCount

src/main/scala/tigerjython/syntaxsupport/struct/StructElement.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ abstract class StructElement {
9090
else
9191
null
9292

93-
protected def getToken(relIndex: Int): Token =
93+
def getToken(relIndex: Int): Token =
9494
_getToken(this.index + relIndex)
9595

9696
def handleMessage(relIndex: Int, message: TokenChangeMessage): Unit

src/main/scala/tigerjython/syntaxsupport/struct/StructLine.scala

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,11 @@ class StructLine extends StructContainer {
121121
}
122122

123123
protected def invalidateAstNode(): Unit = {
124+
//StructLine.linesWithInvalidatedAstNodes += this
125+
validateAstNode()
126+
}
127+
128+
protected def validateAstNode(): Unit = {
124129
val parser = getParser
125130
if (parser != null)
126131
astNode = parser.parse(this)
@@ -163,5 +168,13 @@ class StructLine extends StructContainer {
163168
}
164169
object StructLine {
165170

171+
private val linesWithInvalidatedAstNodes = collection.mutable.Set[StructLine]()
172+
166173
def apply(): StructLine = new StructLine()
174+
175+
def validateAstNodes(): Unit = {
176+
/*for (line <- linesWithInvalidatedAstNodes)
177+
line.validateAstNode()
178+
linesWithInvalidatedAstNodes.clear()*/
179+
}
167180
}

src/main/scala/tigerjython/syntaxsupport/struct/StructProgram.scala

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,20 @@ class StructProgram(val document: SyntaxDocument) extends StructContainer {
6363
protected def createParser(): StmtParser =
6464
new PythonStmtParser(document)
6565

66+
def dump(): Unit = {
67+
println("=" * 70)
68+
for ((child, j) <- children.zipWithIndex)
69+
child match {
70+
case line: StructLine =>
71+
println(s"LINE $j: ")
72+
for (i <- 0 until line.length)
73+
print(line.getToken(i))
74+
println()
75+
case _ =>
76+
println(s"LINE $j: ???")
77+
}
78+
}
79+
6680
override protected def getDocument: SyntaxDocument = document
6781

6882
def getFirstLineOfBlock(lineNo: Int): Int = {
@@ -100,7 +114,7 @@ class StructProgram(val document: SyntaxDocument) extends StructContainer {
100114
// Check for the special case where we are on the 'head' line of a composite statement
101115
if (i == lineNo && i+1 < children.length && this(i+1).indent > indent) {
102116
i = lineNo + 1
103-
val indent = this(i+1).indent
117+
val indent = this(i).indent
104118
while (i < children.length && this(i).hasIndent(indent))
105119
i += 1
106120
(getPhysicalLineOf(lineNo), getPhysicalLineOf(i))

0 commit comments

Comments
 (0)