add page: differences to other languages
This commit is contained in:
parent
5755dc5adf
commit
4f7be96abe
|
@ -19,7 +19,7 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout your repository using git
|
- name: Checkout your repository using git
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
- name: Install, build, and upload your site output
|
- name: Install, build, and upload your site output
|
||||||
uses: withastro/action@v2
|
uses: withastro/action@v2
|
||||||
with:
|
with:
|
||||||
|
|
|
@ -5,6 +5,7 @@ import shikiConfig from './src/utils/shiki';
|
||||||
|
|
||||||
// https://astro.build/config
|
// https://astro.build/config
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
|
site: "https://shulkerscript.hoelting.dev",
|
||||||
integrations: [
|
integrations: [
|
||||||
starlight({
|
starlight({
|
||||||
title: 'ShulkerScript',
|
title: 'ShulkerScript',
|
||||||
|
@ -49,6 +50,13 @@ export default defineConfig({
|
||||||
de: 'Anleitungen',
|
de: 'Anleitungen',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: 'Differences to other languages',
|
||||||
|
link: '/differences',
|
||||||
|
translations: {
|
||||||
|
de: 'Unterschiede zu anderen Sprachen',
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: 'Roadmap',
|
label: 'Roadmap',
|
||||||
link: '/roadmap',
|
link: '/roadmap',
|
||||||
|
|
10
package.json
10
package.json
|
@ -10,11 +10,11 @@
|
||||||
"astro": "astro"
|
"astro": "astro"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astrojs/check": "^0.5.10",
|
"@astrojs/check": "^0.9.3",
|
||||||
"@astrojs/starlight": "^0.24.2",
|
"@astrojs/starlight": "^0.26.1",
|
||||||
"astro": "^4.10.2",
|
"astro": "^4.14.3",
|
||||||
"sharp": "^0.32.6",
|
"sharp": "^0.33.5",
|
||||||
"starlight-links-validator": "^0.7.1",
|
"starlight-links-validator": "^0.10.1",
|
||||||
"typescript": "^5.4.5"
|
"typescript": "^5.4.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
1924
pnpm-lock.yaml
1924
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,23 @@
|
||||||
|
---
|
||||||
|
export interface Props {
|
||||||
|
gap: string
|
||||||
|
}
|
||||||
|
const { gap = "0px" } = Astro.props;
|
||||||
|
---
|
||||||
|
|
||||||
|
<div class="split-view" style={`gap: ${gap}`}>
|
||||||
|
<slot name="left" />
|
||||||
|
<slot name="right" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.split-view {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.split-view > :global(*) {
|
||||||
|
overflow: hidden;
|
||||||
|
margin-top: 0 !important;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,313 @@
|
||||||
|
---
|
||||||
|
title: Differences to other languages
|
||||||
|
description: See how ShulkerScript compares to similar languages.
|
||||||
|
---
|
||||||
|
|
||||||
|
import { Tabs, TabItem, Aside } from "@astrojs/starlight/components";
|
||||||
|
import SplitView from "@components/SplitView.astro";
|
||||||
|
|
||||||
|
This page will highlight the differences of ShulkerScript and vanilla mcfunction and mcscript.
|
||||||
|
|
||||||
|
:::note[Credits to mcscript]
|
||||||
|
I came into contact with [mcscript](https://mcscript.stevertus.com/) by Stevertus a few years ago. I wrote some datapacks
|
||||||
|
with it myself and it was mostly great, but I came across some of its flaws. This inspired me to write my own
|
||||||
|
language, even if it is years later. This comparison should not be seen as too critical, as it seems to be not
|
||||||
|
under active development anymore. If you are interested, check out his new project [objD](https://objd.stevertus.com).
|
||||||
|
:::
|
||||||
|
|
||||||
|
## Functions
|
||||||
|
|
||||||
|
The first difference you can see is that ShulkerScript takes a much different approach to functions than mcfunction and mcscript.
|
||||||
|
The following example declares two functions and calls them second from the first one.
|
||||||
|
|
||||||
|
<SplitView gap="10px">
|
||||||
|
<Tabs slot="left">
|
||||||
|
<TabItem label="ShulkerScript">
|
||||||
|
```shulkerscript
|
||||||
|
// functions.shu
|
||||||
|
#[load]
|
||||||
|
fn first() {
|
||||||
|
second();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn second() {
|
||||||
|
/say Hello, world!
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</TabItem>
|
||||||
|
</Tabs>
|
||||||
|
<Tabs slot="right" syncKey="comparisonLang" style="margin-top: 0">
|
||||||
|
<TabItem label="mcfunction">
|
||||||
|
```mcfunction
|
||||||
|
# functions/first.mcfunction
|
||||||
|
function functions:second
|
||||||
|
```
|
||||||
|
```mcfunction
|
||||||
|
# functions/second.mcfunction
|
||||||
|
say Hello, world!
|
||||||
|
```
|
||||||
|
</TabItem>
|
||||||
|
<TabItem label="mcscript">
|
||||||
|
```mcscript
|
||||||
|
// functions.mcscript
|
||||||
|
#file: first
|
||||||
|
/function functions:second
|
||||||
|
|
||||||
|
#file: second
|
||||||
|
/say Hello, world!
|
||||||
|
```
|
||||||
|
or alternatively
|
||||||
|
```mcscript
|
||||||
|
// functions.mcscript
|
||||||
|
#file: first
|
||||||
|
run function second {
|
||||||
|
/say Hello, world!
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</TabItem>
|
||||||
|
</Tabs>
|
||||||
|
|
||||||
|
</SplitView>
|
||||||
|
|
||||||
|
## Comments
|
||||||
|
|
||||||
|
Comments can be expressed differently in all three languages. ShulkerScript uses `//` for single-line comments and `/* */` for multi-line comments.
|
||||||
|
|
||||||
|
<SplitView gap="10px">
|
||||||
|
<Tabs slot="left">
|
||||||
|
<TabItem label="ShulkerScript">
|
||||||
|
```shulkerscript
|
||||||
|
// comments.shu
|
||||||
|
// this is a comment
|
||||||
|
fn hello() {
|
||||||
|
/// this comment will be included in the compiled output
|
||||||
|
/*
|
||||||
|
this is a
|
||||||
|
multi-line comment
|
||||||
|
*/
|
||||||
|
/say Hello, world!
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</TabItem>
|
||||||
|
</Tabs>
|
||||||
|
<Tabs slot="right" syncKey="comparisonLang" style="margin-top: 0">
|
||||||
|
<TabItem label="mcfunction">
|
||||||
|
```mcfunction
|
||||||
|
# comments.mcfunction
|
||||||
|
# this is a comment
|
||||||
|
# multiline comments have to be done manually
|
||||||
|
say Hello, world!
|
||||||
|
```
|
||||||
|
</TabItem>
|
||||||
|
<TabItem label="mcscript">
|
||||||
|
```mcscript
|
||||||
|
// comments.mcscript
|
||||||
|
// this is a regular comment
|
||||||
|
# this comment will be included in the compiled output
|
||||||
|
/*
|
||||||
|
this is a
|
||||||
|
multi-line comment
|
||||||
|
*/
|
||||||
|
/say Hello, world!
|
||||||
|
```
|
||||||
|
</TabItem>
|
||||||
|
</Tabs>
|
||||||
|
</SplitView>
|
||||||
|
|
||||||
|
## Execute Command
|
||||||
|
|
||||||
|
Both ShulkerScript and mcfunction offer an easier way for using execute commands, as they are common when building datapacks.
|
||||||
|
|
||||||
|
<SplitView gap="10px">
|
||||||
|
<Tabs slot="left">
|
||||||
|
<TabItem label="ShulkerScript">
|
||||||
|
```shulkerscript
|
||||||
|
// execute.shu
|
||||||
|
fn execute() {
|
||||||
|
as("@a"), at("@s") {
|
||||||
|
/setblock ~ ~-1 ~ minecraft:stone
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</TabItem>
|
||||||
|
</Tabs>
|
||||||
|
<Tabs slot="right" syncKey="comparisonLang" style="margin-top: 0">
|
||||||
|
<TabItem label="mcfunction">
|
||||||
|
```mcfunction
|
||||||
|
# execute.mcfunction
|
||||||
|
execute as @a at @s run setblock ~ ~-1 ~ minecraft:stone
|
||||||
|
```
|
||||||
|
</TabItem>
|
||||||
|
<TabItem label="mcscript">
|
||||||
|
```mcscript
|
||||||
|
// execute.mcscript
|
||||||
|
as(@a), at(@s) {
|
||||||
|
/setblock ~ ~-1 ~ minecraft:stone
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</TabItem>
|
||||||
|
</Tabs>
|
||||||
|
</SplitView>
|
||||||
|
|
||||||
|
:::tip
|
||||||
|
ShulkerScript and mcscript each offer a shortcut for `as(...), at("@s")`, which is `asat(@a)`.
|
||||||
|
:::
|
||||||
|
|
||||||
|
## If-Else Statements
|
||||||
|
|
||||||
|
ShulkerScript and mcscript offer a more convenient way to write if-else statements.
|
||||||
|
|
||||||
|
<SplitView gap="10px">
|
||||||
|
<Tabs slot="left">
|
||||||
|
<TabItem label="ShulkerScript">
|
||||||
|
```shulkerscript
|
||||||
|
// conditionals.shu
|
||||||
|
fn conditional() {
|
||||||
|
if("entity @p[distance=..5]") {
|
||||||
|
/say You are close!
|
||||||
|
} else {
|
||||||
|
/say You are far away!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</TabItem>
|
||||||
|
</Tabs>
|
||||||
|
<div slot="right">
|
||||||
|
<Tabs syncKey="comparisonLang" style="margin-top: 0">
|
||||||
|
<TabItem label="mcfunction">
|
||||||
|
```mcfunction
|
||||||
|
# conditionals.mcfunction
|
||||||
|
execute if entity @p[distance=..5] run say You are close!
|
||||||
|
execute unless entity @p[distance=..5] run say You are far away!
|
||||||
|
```
|
||||||
|
</TabItem>
|
||||||
|
<TabItem label="mcscript">
|
||||||
|
```mcscript
|
||||||
|
// conditionals.mcscript
|
||||||
|
if('entity @p[distance=..5]') {
|
||||||
|
/say You are close!
|
||||||
|
} else {
|
||||||
|
/say You are far away!
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</TabItem>
|
||||||
|
</Tabs>
|
||||||
|
<Aside type="caution" title="This does not always work!">
|
||||||
|
When working with else statements and/or commands that could affect the condition,
|
||||||
|
both the mcfunction and mcscript approach could lead to unexpected results as explained [here](#interfering-with-conditions).
|
||||||
|
</Aside>
|
||||||
|
</div>
|
||||||
|
</SplitView>
|
||||||
|
|
||||||
|
### Logical operators
|
||||||
|
|
||||||
|
Both in ShulkerScript and mcscript, logical operators can be used to combine multiple conditions.
|
||||||
|
In the following code examples, the conditions are represented by `A`, `B`, etc. as placeholders for real conditions.
|
||||||
|
|
||||||
|
<SplitView gap="10px">
|
||||||
|
<div slot="left">
|
||||||
|
<Tabs>
|
||||||
|
<TabItem label="ShulkerScript">
|
||||||
|
```shulkerscript
|
||||||
|
// logical-operators.shu
|
||||||
|
fn logical() {
|
||||||
|
if("A" && "B" || "C") {
|
||||||
|
/say A and B or C
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</TabItem>
|
||||||
|
</Tabs>
|
||||||
|
The AND operator takes precedence over the OR operator, so the above code is equivalent to `A && (B || C)`.
|
||||||
|
The code is compiled in such a way, that regardless in which way the expression is true, the command will only run once.
|
||||||
|
</div>
|
||||||
|
<div slot="right">
|
||||||
|
<Tabs syncKey="comparisonLang" style="margin-top: 0">
|
||||||
|
<TabItem label="mcfunction">
|
||||||
|
```mcfunction
|
||||||
|
# logical-operators.mcfunction
|
||||||
|
execute if A if B run say A and B or C
|
||||||
|
execute if C run say A and B or C
|
||||||
|
```
|
||||||
|
<Aside>
|
||||||
|
This leads to a lot of repetition, especially when combining AND and OR operators.
|
||||||
|
</Aside>
|
||||||
|
</TabItem>
|
||||||
|
<TabItem label="mcscript">
|
||||||
|
```mcscript
|
||||||
|
// logical-operators.mcscript
|
||||||
|
if(('A' && 'B') || 'C') {
|
||||||
|
/say A and B or C
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</TabItem>
|
||||||
|
</Tabs>
|
||||||
|
<Aside type="caution" title="">
|
||||||
|
If both `A && B` and `C` are true, the command will run two times!
|
||||||
|
</Aside>
|
||||||
|
</div>
|
||||||
|
</SplitView>
|
||||||
|
|
||||||
|
### Interfering with conditions
|
||||||
|
|
||||||
|
The difference between ShulkerScript and the other two languages is that ShulkerScript compiles to a version
|
||||||
|
where it is not possible to trigger both the if and the else statement at the same time.
|
||||||
|
This could be possible when using the trivial approach to translate the following to mcfunction:
|
||||||
|
|
||||||
|
```shulkerscript
|
||||||
|
// this-works.shu
|
||||||
|
fn thisWorks() {
|
||||||
|
if("block ~ ~-1 ~ minecraft:stone") {
|
||||||
|
/setblock ~ ~-1 ~ minecraft:cobblestone
|
||||||
|
} else {
|
||||||
|
/say Not on stone
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Here the second command will always be executed, because if the condition is true, the command runs and falsifies the condition.
|
||||||
|
When the inverted condition is checked in the second command, it will always be true.
|
||||||
|
ShulkerScript fixes this by using a mix of anonymous functions and data stores.
|
||||||
|
|
||||||
|
```mcfunction
|
||||||
|
# this-is-incorrect.mcfunction
|
||||||
|
execute if block ~ ~-1 ~ minecraft:stone run setblock ~ ~-1 ~ minecraft:cobblestone
|
||||||
|
# this will always execute because after the first command the block cannot be stone
|
||||||
|
execute unless block ~ ~-1 ~ minecraft:stone run say Not on stone
|
||||||
|
```
|
||||||
|
|
||||||
|
## Embedded Programming Language
|
||||||
|
|
||||||
|
ShulkerScript allows running Lua code during compilation and running the returned output in Minecraft, mcscript offers JavaScript modals._createMdxContent
|
||||||
|
|
||||||
|
<SplitView gap="10px">
|
||||||
|
<Tabs slot="left">
|
||||||
|
<TabItem label="ShulkerScript">
|
||||||
|
```shulkerscript
|
||||||
|
// lua.shu
|
||||||
|
fn execute() {
|
||||||
|
run lua() {
|
||||||
|
return "say hi";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</TabItem>
|
||||||
|
</Tabs>
|
||||||
|
<Tabs slot="right" syncKey="comparisonLang" style="margin-top: 0">
|
||||||
|
<TabItem label="mcfunction">
|
||||||
|
<Aside>
|
||||||
|
Vanilla mcfunction does not support embedded programming languages.
|
||||||
|
</Aside>
|
||||||
|
</TabItem>
|
||||||
|
<TabItem label="mcscript">
|
||||||
|
```mcscript
|
||||||
|
// javascript.mcscript
|
||||||
|
modaljs newModal(){
|
||||||
|
return "say hi";
|
||||||
|
}
|
||||||
|
|
||||||
|
newModal()
|
||||||
|
```
|
||||||
|
</TabItem>
|
||||||
|
</Tabs>
|
||||||
|
</SplitView>
|
|
@ -1,3 +1,12 @@
|
||||||
{
|
{
|
||||||
"extends": "astro/tsconfigs/strict"
|
"extends": "astro/tsconfigs/strict",
|
||||||
|
"compilerOptions": {
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"@*": ["src/*"]
|
||||||
|
},
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"jsxImportSource": "react"
|
||||||
|
},
|
||||||
|
"exclude": ["dist", "node_modules"]
|
||||||
}
|
}
|
Loading…
Reference in New Issue