diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 281de45..aa5814c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -25,3 +25,8 @@ jobs: with: name: pork-jar path: tool/build/libs/pork-all.jar + - name: Archive Idea Plugin + uses: actions/upload-artifact@v3 + with: + name: pork-idea + path: support/pork-idea/build/distributions/Pork.zip diff --git a/evaluator/src/main/kotlin/gay/pizza/pork/evaluator/EvaluationVisitor.kt b/evaluator/src/main/kotlin/gay/pizza/pork/evaluator/EvaluationVisitor.kt index a92d449..2b6f132 100644 --- a/evaluator/src/main/kotlin/gay/pizza/pork/evaluator/EvaluationVisitor.kt +++ b/evaluator/src/main/kotlin/gay/pizza/pork/evaluator/EvaluationVisitor.kt @@ -107,8 +107,8 @@ class EvaluationVisitor(root: Scope) : NodeVisitor { subtract = { a, b -> a - b }, multiply = { a, b -> a * b }, divide = { a, b -> a / b }, - euclideanModulo = { a, b -> throw RuntimeException("Can't perform integer modulo between floating point types") }, - remainder = { a, b -> throw RuntimeException("Can't perform integer remainder between floating point types") } + euclideanModulo = { _, _ -> throw RuntimeException("Can't perform integer modulo between floating point types") }, + remainder = { _, _ -> throw RuntimeException("Can't perform integer remainder between floating point types") } ) } @@ -122,8 +122,8 @@ class EvaluationVisitor(root: Scope) : NodeVisitor { subtract = { a, b -> a - b }, multiply = { a, b -> a * b }, divide = { a, b -> a / b }, - euclideanModulo = { a, b -> throw RuntimeException("Can't perform integer modulo between floating point types") }, - remainder = { a, b -> throw RuntimeException("Can't perform integer remainder between floating point types") } + euclideanModulo = { _, _ -> throw RuntimeException("Can't perform integer modulo between floating point types") }, + remainder = { _, _ -> throw RuntimeException("Can't perform integer remainder between floating point types") } ) } diff --git a/gradle.properties b/gradle.properties index a7efeab..2b17698 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1,2 @@ org.gradle.warning.mode=none +kotlin.stdlib.default.dependency=false diff --git a/parser/src/main/kotlin/gay/pizza/pork/parser/StringCharSource.kt b/parser/src/main/kotlin/gay/pizza/pork/parser/StringCharSource.kt index 081757d..a7aa156 100644 --- a/parser/src/main/kotlin/gay/pizza/pork/parser/StringCharSource.kt +++ b/parser/src/main/kotlin/gay/pizza/pork/parser/StringCharSource.kt @@ -1,12 +1,16 @@ package gay.pizza.pork.parser -class StringCharSource(val input: String) : CharSource { - private var index = 0 +class StringCharSource( + val input: CharSequence, + val startIndex: Int = 0, + val endIndex: Int = input.length - 1 +) : CharSource { + private var index = startIndex override val currentIndex: Int get() = index override fun next(): Char { - if (index == input.length) { + if (index == endIndex) { return CharSource.NullChar } val char = input[index] @@ -15,7 +19,7 @@ class StringCharSource(val input: String) : CharSource { } override fun peek(): Char { - if (index == input.length) { + if (index == endIndex) { return CharSource.NullChar } return input[index] diff --git a/settings.gradle.kts b/settings.gradle.kts index dc40cb8..bf388b3 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -10,5 +10,6 @@ include( ":evaluator", ":stdlib", ":ffi", - ":tool" + ":tool", + ":support:pork-idea" ) diff --git a/support/pork-idea/build.gradle.kts b/support/pork-idea/build.gradle.kts new file mode 100644 index 0000000..2ed5116 --- /dev/null +++ b/support/pork-idea/build.gradle.kts @@ -0,0 +1,34 @@ +plugins { + id("org.jetbrains.intellij") version "1.15.0" + id("gay.pizza.pork.module") +} + +dependencies { + implementation(project(":parser")) +} + +intellij { + pluginName.set(properties["pluginName"].toString()) + version.set(properties["platformVersion"].toString()) + type.set(properties["platformType"].toString()) +} + +tasks { + buildSearchableOptions { + enabled = false + } + + patchPluginXml { + version.set(project.properties["pluginVersion"].toString()) + sinceBuild.set(project.properties["pluginSinceBuild"].toString()) + untilBuild.set(project.properties["pluginUntilBuild"].toString()) + pluginDescription.set("Pork Language support for IntelliJ IDEs") + } +} + +project.afterEvaluate { + tasks.buildPlugin { + exclude("**/lib/annotations*.jar") + exclude("**/lib/kotlin*.jar") + } +} diff --git a/support/pork-idea/gradle.properties b/support/pork-idea/gradle.properties new file mode 100644 index 0000000..b7b86cc --- /dev/null +++ b/support/pork-idea/gradle.properties @@ -0,0 +1,13 @@ +# IntelliJ Platform Artifacts Repositories -> https://plugins.jetbrains.com/docs/intellij/intellij-artifacts.html +pluginGroup = gay.pizza.plugins.pork +pluginName = Pork +pluginRepositoryUrl = https://github.com/GayPizzaSpecifications/pork +pluginVersion = 0.1.0 + +# Supported build number ranges and IntelliJ Platform versions -> https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html +pluginSinceBuild = 232 +pluginUntilBuild = 232.* + +# IntelliJ Platform Properties -> https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html#configuration-intellij-extension +platformType = IC +platformVersion = 2023.2.1 diff --git a/support/pork-idea/src/main/kotlin/gay/pizza/pork/intellij/PorkFileType.kt b/support/pork-idea/src/main/kotlin/gay/pizza/pork/intellij/PorkFileType.kt new file mode 100644 index 0000000..3a8714a --- /dev/null +++ b/support/pork-idea/src/main/kotlin/gay/pizza/pork/intellij/PorkFileType.kt @@ -0,0 +1,26 @@ +package gay.pizza.pork.intellij + +import com.intellij.openapi.fileTypes.LanguageFileType +import com.intellij.openapi.util.NlsContexts +import com.intellij.openapi.util.NlsSafe +import org.jetbrains.annotations.NonNls +import javax.swing.Icon + +@Suppress("UnstableApiUsage") +object PorkFileType : LanguageFileType(PorkLanguage) { + override fun getName(): @NonNls String { + return "Pork File" + } + + override fun getDescription(): @NlsContexts.Label String { + return "Pork file" + } + + override fun getDefaultExtension(): @NlsSafe String { + return "pork" + } + + override fun getIcon(): Icon { + return PorkIcon + } +} diff --git a/support/pork-idea/src/main/kotlin/gay/pizza/pork/intellij/PorkIcons.kt b/support/pork-idea/src/main/kotlin/gay/pizza/pork/intellij/PorkIcons.kt new file mode 100644 index 0000000..2555a38 --- /dev/null +++ b/support/pork-idea/src/main/kotlin/gay/pizza/pork/intellij/PorkIcons.kt @@ -0,0 +1,6 @@ +package gay.pizza.pork.intellij + +import com.intellij.openapi.util.IconLoader.getIcon +import javax.swing.Icon + +val PorkIcon: Icon = getIcon("/icons/pork.png", PorkLanguage::class.java) diff --git a/support/pork-idea/src/main/kotlin/gay/pizza/pork/intellij/PorkLanguage.kt b/support/pork-idea/src/main/kotlin/gay/pizza/pork/intellij/PorkLanguage.kt new file mode 100644 index 0000000..d85dbd6 --- /dev/null +++ b/support/pork-idea/src/main/kotlin/gay/pizza/pork/intellij/PorkLanguage.kt @@ -0,0 +1,5 @@ +package gay.pizza.pork.intellij + +import com.intellij.lang.Language + +object PorkLanguage : Language("Pork") diff --git a/support/pork-idea/src/main/kotlin/gay/pizza/pork/intellij/PorkLexer.kt b/support/pork-idea/src/main/kotlin/gay/pizza/pork/intellij/PorkLexer.kt new file mode 100644 index 0000000..de48b6f --- /dev/null +++ b/support/pork-idea/src/main/kotlin/gay/pizza/pork/intellij/PorkLexer.kt @@ -0,0 +1,96 @@ +package gay.pizza.pork.intellij + +import com.intellij.lexer.LexerBase +import com.intellij.openapi.diagnostic.Logger +import com.intellij.openapi.progress.ProcessCanceledException +import com.intellij.psi.tree.IElementType +import gay.pizza.pork.parser.* +import com.intellij.psi.TokenType as PsiTokenType + +class PorkLexer : LexerBase() { + private val log: Logger = Logger.getInstance(PorkLexer::class.java) + + private lateinit var source: StringCharSource + private lateinit var tokenizer: Tokenizer + private var internalTokenStart: Int = 0 + private var internalTokenEnd: Int = 0 + private var internalState: Int = 0 + private var currentTokenType: IElementType? = null + + override fun start(buffer: CharSequence, startOffset: Int, endOffset: Int, initialState: Int) { + source = StringCharSource( + input = buffer, + startIndex = startOffset, + endIndex = endOffset + ) + tokenizer = Tokenizer(source) + internalState = initialState + internalTokenStart = startOffset + internalTokenEnd = startOffset + currentTokenType = null + advance() + } + + override fun getState(): Int { + return internalState + } + + override fun getTokenType(): IElementType? { + return currentTokenType + } + + override fun getTokenStart(): Int { + return internalTokenStart + } + + override fun getTokenEnd(): Int { + return internalTokenEnd + } + + override fun advance() { + internalTokenStart = internalTokenEnd + if (internalTokenStart == bufferEnd) { + currentTokenType = null + return + } + + try { + val currentToken = tokenizer.next() + currentTokenType = tokenAsElement(currentToken) + internalTokenStart = currentToken.start + internalTokenEnd = currentToken.start + currentToken.text.length + } catch (e: ProcessCanceledException) { + throw e + } catch (e: Throwable) { + currentTokenType = PsiTokenType.BAD_CHARACTER + internalTokenEnd = bufferEnd + log.warn(Tokenizer::class.java.name, e) + } + } + + override fun getBufferSequence(): CharSequence { + return source.input + } + + override fun getBufferEnd(): Int { + return source.endIndex + } + + private fun tokenAsElement(token: Token): IElementType = when { + token.type.family == TokenFamily.KeywordFamily -> + PorkTokenTypes.Keyword + token.type.family == TokenFamily.SymbolFamily -> + PorkTokenTypes.Symbol + token.type.family == TokenFamily.OperatorFamily -> + PorkTokenTypes.Operator + token.type.family == TokenFamily.StringLiteralFamily -> + PorkTokenTypes.String + token.type.family == TokenFamily.NumericLiteralFamily -> + PorkTokenTypes.Number + token.type == TokenType.Whitespace -> + PorkTokenTypes.Whitespace + else -> PsiTokenType.CODE_FRAGMENT + } + + override fun toString(): String = "Lexer(start=$internalTokenStart, end=$internalTokenEnd)" +} diff --git a/support/pork-idea/src/main/kotlin/gay/pizza/pork/intellij/PorkSyntaxHighlighter.kt b/support/pork-idea/src/main/kotlin/gay/pizza/pork/intellij/PorkSyntaxHighlighter.kt new file mode 100644 index 0000000..fdb7dda --- /dev/null +++ b/support/pork-idea/src/main/kotlin/gay/pizza/pork/intellij/PorkSyntaxHighlighter.kt @@ -0,0 +1,49 @@ +package gay.pizza.pork.intellij + +import com.intellij.lexer.Lexer +import com.intellij.openapi.editor.DefaultLanguageHighlighterColors +import com.intellij.openapi.editor.colors.TextAttributesKey +import com.intellij.openapi.fileTypes.SyntaxHighlighter +import com.intellij.openapi.fileTypes.SyntaxHighlighterBase +import com.intellij.psi.tree.IElementType + +object PorkSyntaxHighlighter : SyntaxHighlighter { + override fun getHighlightingLexer(): Lexer { + return PorkLexer() + } + + override fun getTokenHighlights(tokenType: IElementType?): Array { + if (tokenType == null) return emptyArray() + val attributes = when (tokenType) { + PorkTokenTypes.Keyword -> + TextAttributesKey.createTextAttributesKey( + "PORK.KEYWORD", + DefaultLanguageHighlighterColors.KEYWORD + ) + PorkTokenTypes.Symbol -> + TextAttributesKey.createTextAttributesKey( + "PORK.SYMBOL", + DefaultLanguageHighlighterColors.LOCAL_VARIABLE + ) + PorkTokenTypes.Operator -> + TextAttributesKey.createTextAttributesKey( + "PORK.OPERATOR", + DefaultLanguageHighlighterColors.OPERATION_SIGN + ) + PorkTokenTypes.String -> + TextAttributesKey.createTextAttributesKey( + "PORK.STRING", + DefaultLanguageHighlighterColors.STRING + ) + PorkTokenTypes.Number -> + TextAttributesKey.createTextAttributesKey( + "PORK.NUMBER", + DefaultLanguageHighlighterColors.NUMBER + ) + else -> null + } + return if (attributes == null) + emptyArray() + else SyntaxHighlighterBase.pack(attributes) + } +} diff --git a/support/pork-idea/src/main/kotlin/gay/pizza/pork/intellij/PorkSyntaxHighlighterFactory.kt b/support/pork-idea/src/main/kotlin/gay/pizza/pork/intellij/PorkSyntaxHighlighterFactory.kt new file mode 100644 index 0000000..a900cd7 --- /dev/null +++ b/support/pork-idea/src/main/kotlin/gay/pizza/pork/intellij/PorkSyntaxHighlighterFactory.kt @@ -0,0 +1,12 @@ +package gay.pizza.pork.intellij + +import com.intellij.openapi.fileTypes.SyntaxHighlighter +import com.intellij.openapi.fileTypes.SyntaxHighlighterFactory +import com.intellij.openapi.project.Project +import com.intellij.openapi.vfs.VirtualFile + +class PorkSyntaxHighlighterFactory : SyntaxHighlighterFactory() { + override fun getSyntaxHighlighter(project: Project?, virtualFile: VirtualFile?): SyntaxHighlighter { + return PorkSyntaxHighlighter + } +} diff --git a/support/pork-idea/src/main/kotlin/gay/pizza/pork/intellij/PorkTokenTypes.kt b/support/pork-idea/src/main/kotlin/gay/pizza/pork/intellij/PorkTokenTypes.kt new file mode 100644 index 0000000..0785ad3 --- /dev/null +++ b/support/pork-idea/src/main/kotlin/gay/pizza/pork/intellij/PorkTokenTypes.kt @@ -0,0 +1,13 @@ +package gay.pizza.pork.intellij + +import com.intellij.psi.TokenType +import com.intellij.psi.tree.IElementType + +object PorkTokenTypes { + val Whitespace = TokenType.WHITE_SPACE + val Keyword = IElementType("keyword", PorkLanguage) + val Symbol = IElementType("symbol", PorkLanguage) + val Operator = IElementType("operator", PorkLanguage) + val String = IElementType("string", PorkLanguage) + val Number = IElementType("number", PorkLanguage) +} diff --git a/support/pork-idea/src/main/resources/META-INF/plugin.xml b/support/pork-idea/src/main/resources/META-INF/plugin.xml new file mode 100644 index 0000000..3248540 --- /dev/null +++ b/support/pork-idea/src/main/resources/META-INF/plugin.xml @@ -0,0 +1,18 @@ + + + gay.pizza.plugins.pork + Pork + Languages + Gay Pizza Specifications + com.intellij.modules.platform + + + + + + + + diff --git a/support/pork-idea/src/main/resources/META-INF/pluginIcon.svg b/support/pork-idea/src/main/resources/META-INF/pluginIcon.svg new file mode 100644 index 0000000..8026f06 --- /dev/null +++ b/support/pork-idea/src/main/resources/META-INF/pluginIcon.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/support/pork-idea/src/main/resources/icons/pork.png b/support/pork-idea/src/main/resources/icons/pork.png new file mode 100644 index 0000000..29e1517 Binary files /dev/null and b/support/pork-idea/src/main/resources/icons/pork.png differ