Compare commits
No commits in common. "main" and "create-pull-request/patch" have entirely different histories.
main
...
create-pul
|
@ -1,12 +1,10 @@
|
||||||
name: Deploy Docs to Pages
|
name: Publish Docs
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ "main" ]
|
branches: [ "main" ]
|
||||||
paths:
|
paths:
|
||||||
- src/**
|
|
||||||
- docs/**
|
- docs/**
|
||||||
- .github/workflows/deploy-docs2.yml
|
|
||||||
|
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
|
@ -23,36 +21,8 @@ jobs:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Copy scripts to Docs public assets
|
|
||||||
run: |
|
|
||||||
script_dir=docs/src/.vuepress/public/scripts
|
|
||||||
rm -rf $script_dir
|
|
||||||
mkdir $script_dir
|
|
||||||
cp src/* $script_dir/
|
|
||||||
|
|
||||||
- name: Install Lua
|
|
||||||
uses: ljmf00/setup-lua@v1.0.0
|
|
||||||
with:
|
|
||||||
lua-version: 5.3
|
|
||||||
install-luarocks: true
|
|
||||||
|
|
||||||
- name: Minify Scripts
|
|
||||||
run: |
|
|
||||||
script_dir=docs/src/.vuepress/public/scripts
|
|
||||||
lua minify.lua minify src/movescript.lua > $script_dir/movescript-min.lua
|
|
||||||
lua minify.lua minify src/itemscript.lua > $script_dir/itemscript-min.lua
|
|
||||||
lua minify.lua minify src/buildscript.lua > $script_dir/buildscript-min.lua
|
|
||||||
|
|
||||||
- name: Clean Up Lua Artifacts
|
|
||||||
run: |
|
|
||||||
rm -rf .lua
|
|
||||||
rm -rf .luarocks
|
|
||||||
rm -rf .source
|
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3
|
||||||
with:
|
|
||||||
node-version: 16
|
|
||||||
|
|
||||||
- name: Setup Pages
|
- name: Setup Pages
|
||||||
uses: actions/configure-pages@v2
|
uses: actions/configure-pages@v2
|
||||||
|
@ -80,4 +50,3 @@ jobs:
|
||||||
- name: Deploy to GitHub Pages
|
- name: Deploy to GitHub Pages
|
||||||
id: deployment
|
id: deployment
|
||||||
uses: actions/deploy-pages@v1
|
uses: actions/deploy-pages@v1
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
name: Minify Scripts
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ "main" ]
|
||||||
|
paths:
|
||||||
|
- src/**
|
||||||
|
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Install Lua
|
||||||
|
uses: ljmf00/setup-lua@v1.0.0
|
||||||
|
with:
|
||||||
|
lua-version: 5.3
|
||||||
|
install-luarocks: true
|
||||||
|
|
||||||
|
- name: Minify Scripts
|
||||||
|
run: |
|
||||||
|
rm -rf min
|
||||||
|
mkdir min
|
||||||
|
lua minify.lua minify src/movescript.lua > min/movescript.lua
|
||||||
|
lua minify.lua minify src/itemscript.lua > min/itemscript.lua
|
||||||
|
|
||||||
|
- name: Clean Up Repository
|
||||||
|
run: |
|
||||||
|
rm -rf .lua
|
||||||
|
rm -rf .luarocks
|
||||||
|
rm -rf .source
|
||||||
|
|
||||||
|
- name: Create Pull Request
|
||||||
|
uses: peter-evans/create-pull-request@v4
|
|
@ -8,9 +8,6 @@
|
||||||
"name": "movescript-docs",
|
"name": "movescript-docs",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
|
||||||
"vuepress-plugin-code-copy": "^1.0.6"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"vuepress": "^1.5.3"
|
"vuepress": "^1.5.3"
|
||||||
}
|
}
|
||||||
|
@ -14225,12 +14222,6 @@
|
||||||
"object.getownpropertydescriptors": "^2.0.3"
|
"object.getownpropertydescriptors": "^2.0.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/vuepress-plugin-code-copy": {
|
|
||||||
"version": "1.0.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/vuepress-plugin-code-copy/-/vuepress-plugin-code-copy-1.0.6.tgz",
|
|
||||||
"integrity": "sha512-FiqwMtlb4rEsOI56O6sSkekcd3SlESxbkR2IaTIQxsMOMoalKfW5R9WlR1Pjm10v6jmU661Ex8MR11k9IzrNUg==",
|
|
||||||
"license": "GPL-3.0-or-later"
|
|
||||||
},
|
|
||||||
"node_modules/vuepress-plugin-container": {
|
"node_modules/vuepress-plugin-container": {
|
||||||
"version": "2.1.5",
|
"version": "2.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/vuepress-plugin-container/-/vuepress-plugin-container-2.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/vuepress-plugin-container/-/vuepress-plugin-container-2.1.5.tgz",
|
||||||
|
@ -26379,11 +26370,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"vuepress-plugin-code-copy": {
|
|
||||||
"version": "1.0.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/vuepress-plugin-code-copy/-/vuepress-plugin-code-copy-1.0.6.tgz",
|
|
||||||
"integrity": "sha512-FiqwMtlb4rEsOI56O6sSkekcd3SlESxbkR2IaTIQxsMOMoalKfW5R9WlR1Pjm10v6jmU661Ex8MR11k9IzrNUg=="
|
|
||||||
},
|
|
||||||
"vuepress-plugin-container": {
|
"vuepress-plugin-container": {
|
||||||
"version": "2.1.5",
|
"version": "2.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/vuepress-plugin-container/-/vuepress-plugin-container-2.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/vuepress-plugin-container/-/vuepress-plugin-container-2.1.5.tgz",
|
||||||
|
|
|
@ -15,8 +15,5 @@
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"vuepress": "^1.5.3"
|
"vuepress": "^1.5.3"
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"vuepress-plugin-code-copy": "^1.0.6"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,11 +81,5 @@ module.exports = {
|
||||||
plugins: [
|
plugins: [
|
||||||
'@vuepress/plugin-back-to-top',
|
'@vuepress/plugin-back-to-top',
|
||||||
'@vuepress/plugin-medium-zoom',
|
'@vuepress/plugin-medium-zoom',
|
||||||
['vuepress-plugin-code-copy', {
|
|
||||||
backgroundTransition: false,
|
|
||||||
staticIcon: false,
|
|
||||||
color: '#de9502',
|
|
||||||
successText: 'Copied to clipboard.'
|
|
||||||
}]
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
---
|
||||||
|
sidebar: auto
|
||||||
|
---
|
||||||
|
|
||||||
|
# Config
|
||||||
|
|
||||||
|
## foo
|
||||||
|
|
||||||
|
- Type: `string`
|
||||||
|
- Default: `/`
|
||||||
|
|
||||||
|
## bar
|
||||||
|
|
||||||
|
- Type: `string`
|
||||||
|
- Default: `/`
|
|
@ -8,8 +8,3 @@ Movescript is a set of libraries that remove the prohibitive tedium of writing t
|
||||||
| [Itemscript](./itemscript/) | A collection of helper functions that simplify inventory management. |
|
| [Itemscript](./itemscript/) | A collection of helper functions that simplify inventory management. |
|
||||||
|
|
||||||
These modules are intended to be used with the Turtle robots provided by the [CC:Tweaked](https://tweaked.cc/) mod for Minecraft.
|
These modules are intended to be used with the Turtle robots provided by the [CC:Tweaked](https://tweaked.cc/) mod for Minecraft.
|
||||||
|
|
||||||
You can run an installer script to automatically download the latest versions of all of the movescript family of libraries:
|
|
||||||
```shell
|
|
||||||
wget run https://andrewlalis.github.io/movescript/scripts/ms-installer.lua
|
|
||||||
```
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ The Itemscript module provides a flexible, powerful interface for managing a tur
|
||||||
To install this module, run the following command from your turtle's console:
|
To install this module, run the following command from your turtle's console:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
wget https://andrewlalis.github.io/movescript/scripts/itemscript.lua
|
wget https://raw.githubusercontent.com/andrewlalis/movescript/main/min/itemscript.lua
|
||||||
```
|
```
|
||||||
|
|
||||||
And then use it in a script:
|
And then use it in a script:
|
||||||
|
|
|
@ -26,27 +26,6 @@ In the case of strings, a **filter expression string** is a string that can be u
|
||||||
|
|
||||||
The most basic form of an expression string is just an item name, like `"minecraft:dirt"`, or `"create:train_door"`. Most normal items will begin with the `minecraft:` *namespace* prefix. If you don't include such a prefix, and you're not doing a [fuzzy match](#fuzzy-match), itemscript will add `minecraft:` for you.
|
The most basic form of an expression string is just an item name, like `"minecraft:dirt"`, or `"create:train_door"`. Most normal items will begin with the `minecraft:` *namespace* prefix. If you don't include such a prefix, and you're not doing a [fuzzy match](#fuzzy-match), itemscript will add `minecraft:` for you.
|
||||||
|
|
||||||
### Grammar
|
|
||||||
|
|
||||||
Filter expressions can be summarized with a BNF-style grammar description.
|
|
||||||
|
|
||||||
```
|
|
||||||
word = %a[%w%-_:]* A whole or substring of an item's name.
|
|
||||||
number = %d+
|
|
||||||
expr = word Matches item stacks whose name matches the given word.
|
|
||||||
= #word Matches item stacks whose name contains the given word.
|
|
||||||
= (expr) Grouping of a nested expression.
|
|
||||||
= !expr Matches item stacks that don't match the given expression.
|
|
||||||
= expr | expr Matches item stacks that match any of the given expressions (OR).
|
|
||||||
= expr & expr Matches item stacks that match all of the given expressions (AND).
|
|
||||||
= expr > number Matches item stacks that match the given expression, and have more than N items.
|
|
||||||
= expr >= number Matches item stacks that match the given expression, and have more than or equal to N items.
|
|
||||||
= expr < number Matches item stacks that match the given expression, and have less than N items.
|
|
||||||
= expr <= number Matches item stacks that match the given expression, and have less than or equal to N items.
|
|
||||||
= expr = number Matches item stacks that match the given expression, and have exactly N items.
|
|
||||||
= expr != number Matches item stacks that match the given expression, and do not have exactly N items.
|
|
||||||
```
|
|
||||||
|
|
||||||
For example, we can count the number of stone items in our inventory like this:
|
For example, we can count the number of stone items in our inventory like this:
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
|
|
|
@ -7,7 +7,7 @@ The Movescript module provides a simple interface for executing *movescript sour
|
||||||
To install this module, run the following command from your turtle's console:
|
To install this module, run the following command from your turtle's console:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
wget https://andrewlalis.github.io/movescript/scripts/movescript.lua
|
wget https://raw.githubusercontent.com/andrewlalis/movescript/main/min/movescript.lua
|
||||||
```
|
```
|
||||||
|
|
||||||
And then use it in a script:
|
And then use it in a script:
|
||||||
|
|
|
@ -7,48 +7,14 @@ local ms = require("movescript")
|
||||||
ms.run("2F")
|
ms.run("2F")
|
||||||
```
|
```
|
||||||
|
|
||||||
## `parse(script, settings)`
|
## `run(script, settings)`
|
||||||
|
|
||||||
Parses the given `script` string and returns a table containing the parsed instructions to be executed. This is mostly useful for debugging your scripts.
|
|
||||||
|
|
||||||
## `executeInstruction(instruction, settings, preExecuteHook, postExecuteHook)`
|
|
||||||
|
|
||||||
Executes a single instruction table using the given settings, and if pre- and post-execution hooks are defined, they will be invoked. This is mostly useful for debugging your scripts.
|
|
||||||
|
|
||||||
## `run(script, settings, preExecuteHook, postExecuteHook)`
|
|
||||||
|
|
||||||
Runs the given `script` string as a movescript, and optionally a `settings` table can be provided. Otherwise, [default settings](settings.md) will be used.
|
Runs the given `script` string as a movescript, and optionally a `settings` table can be provided. Otherwise, [default settings](settings.md) will be used.
|
||||||
|
|
||||||
For example:
|
## `runFile(filename, settings)`
|
||||||
|
|
||||||
```lua
|
|
||||||
local ms = require("movescript")
|
|
||||||
ms.run("3F2R3B2LUD", {debug=true})
|
|
||||||
```
|
|
||||||
|
|
||||||
If you provide a non-nil `preExecuteHook` or `postExecuteHook` function, that function will run before or after each instruction in the script, respectively. This could be used to update other systems as to the robot's status, or to make sure items are selected.
|
|
||||||
|
|
||||||
## `runFile(filename, settings, preExecuteHook, postExecuteHook)`
|
|
||||||
|
|
||||||
Reads content from the given filename and executes it as a script. Just like with `run`, an optional `settings` table can be provided.
|
Reads content from the given filename and executes it as a script. Just like with `run`, an optional `settings` table can be provided.
|
||||||
|
|
||||||
## `validate(script, settings)`
|
|
||||||
|
|
||||||
Validates the given `script`, by parsing its instructions in a wrapped [`pcall`](https://www.lua.org/pil/8.4.html). It returns `true` if the script is valid, or `false` and an error message describing why the script is not valid.
|
|
||||||
|
|
||||||
For example:
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local ms = require("movescript")
|
|
||||||
local status, message = ms.validate("not a valid script.")
|
|
||||||
```
|
|
||||||
|
|
||||||
## `mirror(script)`
|
|
||||||
|
|
||||||
Mirrors the given `script`. That is, this swaps any `R` (turn right) instructions with `L` (turn left), which effectively mirrors the robot's motion relative to its original facing direction.
|
|
||||||
|
|
||||||
Returns the mirrored script which can then be run.
|
|
||||||
|
|
||||||
## `defaultSettings`
|
## `defaultSettings`
|
||||||
|
|
||||||
A table containing the default [settings](./settings.md) for any script executed by the movescript module.
|
A table containing the default settings for any script executed by the movescript module.
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
Every movescript must follow the outline defined in this specification.
|
Every movescript must follow the outline defined in this specification.
|
||||||
|
|
||||||
Each script consists of zero or more **instructions** or **repeated instructions**, separated by zero or more whitespace characters.
|
Each script consists of zero or more **instructions**, separated by zero or more whitespace characters.
|
||||||
|
|
||||||
## Instructions
|
## Instructions
|
||||||
|
|
||||||
|
@ -10,53 +10,29 @@ An instruction consists of an optional positive integer number, followed by a re
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
-- The regex used to parse instructions.
|
-- The regex used to parse instructions.
|
||||||
instruction = string.find(script, "%s*(%d*%u%l*)%s*")
|
instruction = string.find(script, "%W*(%d*%u%l*)%W*")
|
||||||
```
|
```
|
||||||
|
|
||||||
Each instruction can be split into two parts: the **action**, and the **count**. The action is the textual part of the instruction, and maps to a turtle behavior. The count is the optional numerical part of the instruction, and defaults to `1` if no number is provided.
|
Each instruction can be split into two parts: the **action**, and the **count**. The action is the textual part of the instruction, and maps to a turtle behavior. The count is the optional numerical part of the instruction, and defaults to `1` if no number is provided.
|
||||||
|
|
||||||
Here are some examples of valid instructions: `3F`, `U`, `1R`
|
|
||||||
|
|
||||||
Some instructions may allow you to specify additional options. These can be defined as key-value pairs in parentheses after the action part.
|
|
||||||
|
|
||||||
For example: `4A(delay=0.25, file=tmp.txt)`
|
|
||||||
|
|
||||||
## Repeated Instructions
|
|
||||||
|
|
||||||
A repeated instruction is a grouping of instructions that are repeated a specified number of times. It's denoted as a positive integer number, followed by a series of [instructions](#instructions) within parentheses.
|
|
||||||
|
|
||||||
For example: `22(AF)` - We execute the instructions `A` and `F` 22 times.
|
|
||||||
|
|
||||||
## Actions
|
## Actions
|
||||||
|
|
||||||
The following table lists all actions that are available in Movescript. Attempting to invoke an action not listed here will result in an error that will terminate your script.
|
The following table lists all actions that are available in Movescript. Attempting to invoke an action not listed here will result in an error that will terminate your script.
|
||||||
|
|
||||||
| Action | Description | Options |
|
| Action | Description | Needs Fuel |
|
||||||
| ------ | ------------------------------------------------ | ------------------------------------------ |
|
| ------ | ------------------------------------------------ | ---------- |
|
||||||
| `U` | Move up. |
|
| `U` | Move up. | ✅ |
|
||||||
| `D` | Move down. |
|
| `D` | Move down. | ✅ |
|
||||||
| `L` | Turn left. |
|
| `L` | Turn left. | ❌ |
|
||||||
| `R` | Turn right. |
|
| `R` | Turn right. | ❌ |
|
||||||
| `F` | Move forward. |
|
| `F` | Move forward. | ✅ |
|
||||||
| `B` | Move backward. |
|
| `B` | Move backward. | ✅ |
|
||||||
| `P` | Place the selected item in front of the turtle. | `text: string` - Text to use if placing a sign. |
|
| `P` | Place the selected item in front of the turtle. | ❌ |
|
||||||
| `Pu` | Place the selected item above the turtle. | `text: string` - Text to use if placing a sign. |
|
| `Pu` | Place the selected item above the turtle. | ❌ |
|
||||||
| `Pd` | Place the selected item below the turtle. | `text: string` - Text to use if placing a sign. |
|
| `Pd` | Place the selected item below the turtle. | ❌ |
|
||||||
| `A` | Attack in front of the turtle. | `side: string` - The tool side to use (left or right). |
|
| `A` | Attack in front of the turtle. | ❌ |
|
||||||
| `Au` | Attack above the turtle. | `side: string` - The tool side to use (left or right). |
|
| `Au` | Attack above the turtle. | ❌ |
|
||||||
| `Ad` | Attack below the turtle. | `side: string` - The tool side to use (left or right). |
|
| `Ad` | Attack below the turtle. | ❌ |
|
||||||
| `Dg` | Dig in front of the turtle. | `side: string` - The tool side to use (left or right). |
|
|
||||||
| `Dgu` | Dig above the turtle. | `side: string` - The tool side to use (left or right). |
|
|
||||||
| `Dgd` | Dig below the turtle. | `side: string` - The tool side to use (left or right). |
|
|
||||||
| `S` | Suck items from in front of the turtle. | `count: number` - The number of items to suck. |
|
|
||||||
| `Su` | Suck items from above the turtle. | `count: number` - The number of items to suck. |
|
|
||||||
| `Sd` | Suck items from below the turtle. | `count: number` - The number of items to suck. |
|
|
||||||
| `Eqr` | Equip the selected item to the right side. |
|
|
||||||
| `Eql` | Equip the selected item to the left side. |
|
|
||||||
| `Sel` | Selects slot 1, or the specified slot. | `slot: number` - The slot to select. |
|
|
||||||
| `Dr` | Drops the selected items in front of the turtle. | `count: number` - The number of items to drop. |
|
|
||||||
| `Dru` | Drops the selected items above the turtle. | `count: number` - The number of items to drop. |
|
|
||||||
| `Drd` | Drops the selected items below the turtle. | `count: number` - The number of items to drop. |
|
|
||||||
|
|
||||||
For example, if we want our turtle to go forward 3 times, instead of writing `turtle.forward()` 3 times, we can just do the following:
|
For example, if we want our turtle to go forward 3 times, instead of writing `turtle.forward()` 3 times, we can just do the following:
|
||||||
|
|
||||||
|
@ -79,6 +55,6 @@ The following snippets show a few example scripts, along with a description of w
|
||||||
|
|
||||||
`3F2U1L` - Move forward 3 blocks, then up 2 blocks, and turn left.
|
`3F2U1L` - Move forward 3 blocks, then up 2 blocks, and turn left.
|
||||||
|
|
||||||
`B2RAd` - Move back, then turn right twice, and then attack downward.
|
`B2RAd` - Move back 2 blocks, then turn right twice, and then attack downward.
|
||||||
|
|
||||||
|
|
||||||
`4(U2RD)` - 4 times in a row, move up, twice right, and back down.
|
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
# Scripts
|
|
||||||
|
|
||||||
This directory contains all scripts published under **Movescript**.
|
|
||||||
|
|
||||||
- [movescript](./movescript.lua)
|
|
||||||
- [movescript-min](./movescript-min.lua)
|
|
||||||
- [itemscript](./itemscript.lua)
|
|
||||||
- [itemscript-min](./itemscript-min.lua)
|
|
||||||
- [buildscript](./buildscript.lua)
|
|
||||||
- [buildscript-min](./buildscript-min.lua)
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
--[[
|
||||||
|
Installation script for installing all libraries.
|
||||||
|
|
||||||
|
Run `wget run https://raw.githubusercontent.com/andrewlalis/movescript/main/install.lua`
|
||||||
|
to run the installer on your device.
|
||||||
|
]]--
|
||||||
|
|
||||||
|
BASE_URL = "https://raw.githubusercontent.com/andrewlalis/movescript/main/"
|
||||||
|
|
||||||
|
SCRIPTS = {
|
||||||
|
"movescript.lua",
|
||||||
|
"itemscript.lua"
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Create a local executable to re-install, instead of having to run this file via wget.
|
||||||
|
local f = io.open("install-movescript.lua", "w")
|
||||||
|
for _, script in pairs(SCRIPTS) do
|
||||||
|
url = BASE_URL .. script
|
||||||
|
cmd = "wget " .. url .. " " .. script
|
||||||
|
shell.run(cmd)
|
||||||
|
f:write("if fs.exists(\"" .. script .. "\") then fs.delete(\"" .. script .. "\") end")
|
||||||
|
f:write("shell.run(\"" .. cmd .. "\")")
|
||||||
|
end
|
||||||
|
f:close()
|
|
@ -0,0 +1 @@
|
||||||
|
a="0.0.1"local b=turtle local c={}local function d(k,l,m)return k~=nil and((not m and k.name==l)or string.find(k.name,l))end local function e(k)return function(l)return not k(l)end end local function f(k)return function(l)for m,n in pairs(k)do if not n(l)then return false end end return true end end local function g(k)return function(l)for m,n in pairs(k)do if n(l)then return true end end return false end end local function h(k)local l,m=string.find(k,"^[!#]+")local n=false local o=false if l~=nil then for q=l,m do local r=string.sub(k,q,q)if r=="!"then o=true elseif r=="#"then n=true end end k=string.sub(k,m+1,string.len(k))end local p=string.find(k,":")if p==nil and not n then k="minecraft:"..k end return function(q)if q==nil then return false end local r=d(q,k,n)if o then r=not r end return r end end local function i(k)if type(k)=="table"and#k>0 and type(k[1])=="string"then local l={}for m,n in pairs(k)do table.insert(l,h(n))end return g(l)elseif type(k)=="string"then return h(k)elseif type(k)=="function"then return k else error("Unsupported filter type: "..type(k))end end function c.totalCount(k)local l=i(k)local m=0 for n=1,16 do local o=b.getItemDetail(n)if l(o)then m=m+o.count end end return m end function c.select(k)local l=i(k)for m=1,16 do local n=b.getItemDetail(m)if l(n)then b.select(m)return true end end return false end local function j(k,l)for m=1,16 do local n=b.getItemDetail(m)if l(n)then b.select(m)k()end end end function c.dropAll(k)j(b.drop,i(k))end function c.dropAllDown(k)j(b.dropDown,i(k))end function c.dropAllUp(k)j(b.dropUp,i(k))end function c.organize()error("Not yet implemented.")end return c
|
|
@ -0,0 +1 @@
|
||||||
|
a="0.0.1"local d=turtle local e={}e.defaultSettings={debug=false,safe=true,destructive=false,fuels={"minecraft:coal","minecraft:charcoal"}}local function f(t,u)if u and u.debug then print("[MS] "..t)end end function d.digBack(t)d.turnRight()d.turnRight()d.dig(t)d.turnRight()d.turnRight()end function d.detectBack()d.turnRight()d.turnRight()local t=d.detect()d.turnRight()d.turnRight()return t end local function g(t,u,v,w)w=w or e.defaultSettings b=w.safe or e.defaultSettings.safe c=w.destructive or e.defaultSettings.destructive local x=t()if not b then return end while not x do f("Unable to move.",w)if c and v()then f("Detected a block in the way; attempting to remove it.",w)u()end x=t()end end local function h(t)f("Moving up.",t)g(d.up,d.digUp,d.detectUp,t)end local function j(t)f("Moving down.",t)g(d.down,d.digDown,d.detectDown,t)end local function k(t)f("Moving forward.",t)g(d.forward,d.dig,d.detect,t)end local function l(t)f("Moving back.",t)g(d.back,d.digBack,d.detectBack,t)end local function m(t)f("Turning right.",t)d.turnRight()end local function n(t)f("Turning left.",t)d.turnLeft()end local o={["U"]={f=h,needsFuel=true},["D"]={f=j,needsFuel=true},["L"]={f=n,needsFuel=false},["R"]={f=m,needsFuel=false},["F"]={f=k,needsFuel=true},["B"]={f=l,needsFuel=true},["P"]={f=d.place,needsFuel=false},["Pu"]={f=d.placeUp,needsFuel=false},["Pd"]={f=d.placeDown,needsFuel=false},["A"]={f=d.attack,needsFuel=false},["Au"]={f=d.attackUp,needsFuel=false},["Ad"]={f=d.attackDown,needsFuel=false}}local function p(t)f("Refueling...",t)local u=t.fuels or e.defaultSettings.fuels local v=false for w=1,16 do local x=d.getItemDetail(w)if x~=nil then for y,z in pairs(u)do if x.name==z then d.select(i)if d.refuel(x.count)then v=true end break end end end end return v end local function q(t,u)p(u)while d.getFuelLevel<t do print("[MS] Fuel level is too low. Level: "..d.getFuelLevel()..". Required: "..t..". Please add some of the following fuels:")local v=u.fuels or e.defaultSettings.fuels for x,y in pairs(v)do print(" - "..y)end local w=false while not w do os.pullEvent("turtle_inventory")w=p()end end end local function r(t,u)local v=o[t.action]if v then f("Executing action \""..t.action.."\" "..t.count.." times.",u)local w=((u.safe or true)and(v.needsFuel)and(t.count>d.getFuelLevel()))if w then local x=t.count q(x,u)end for x=1,t.count do v.f()end end end local function s(t,u)local v={}for w in string.gfind(t,"%W*(%d*%u%l*)%W*")do local x,y=string.find(w,"%d+")local z,A=string.find(w,"%u%l*")local B=1 if x~=nil then B=tonumber(string.sub(w,x,y))end local C=string.sub(w,z,A)if B<1 or B>d.getFuelLimit()then error("Instruction at index "..z.." has an invalid count of "..B..". It should be >= 1 and <= "..d.getFuelLimit())end if o[C]==nil then error("Instruction at index "..z..", \""..C.."\", does not refer to a valid action.")end table.insert(v,{action=C,count=B})f("Parsed instruction: "..w,u)end return v end function e.run(t,u)u=u or e.defaultSettings t=t or""f("Executing script: "..t,u)local v=s(t,u)for w,x in pairs(v)do r(x,u)end end function e.runFile(t,u)local v=fs.open(t,"r")local w=v.readAll()v.close()e.run(w,u)end return e
|
|
@ -1,119 +0,0 @@
|
||||||
--[[
|
|
||||||
Buildscript - A unified set of tools that make repetitive building tasks easier
|
|
||||||
with ComputerCraft robots.
|
|
||||||
|
|
||||||
Author: Andrew Lalis <andrewlalisofficial@gmail.com>
|
|
||||||
|
|
||||||
This module depends upon both Movescript and Itemscript.
|
|
||||||
]]--
|
|
||||||
local movescript = require("movescript")
|
|
||||||
local itemscript = require("itemscript")
|
|
||||||
|
|
||||||
-- The buildscript module.
|
|
||||||
local buildscript = {}
|
|
||||||
buildscript.VERSION = "0.0.1"
|
|
||||||
|
|
||||||
-- Runs a movescript script, while ensuring that a given item is always selected.
|
|
||||||
function buildscript.runWithItem(ms_script, filterExpr, settings)
|
|
||||||
movescript.run(ms_script, settings, function() itemscript.selectOrWait(filterExpr) end)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Runs a movescript script, while selecting random items that match a filter.
|
|
||||||
function buildscript.runWithRandomItems(ms_script, filterExpr, settings)
|
|
||||||
movescript.run(ms_script, settings, function() itemscript.selectRandomOrWait(filterExpr) end)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Parses a value for an argument specification from a raw value.
|
|
||||||
local function parseArgValue(argSpec, arg)
|
|
||||||
if argSpec.required and (not arg or #arg < 1) then
|
|
||||||
return false, "Missing required value."
|
|
||||||
end
|
|
||||||
if argSpec.type == "string" then
|
|
||||||
return true, arg
|
|
||||||
elseif argSpec.type == "number" then
|
|
||||||
local num = tonumber(arg)
|
|
||||||
if not num and argSpec.required then
|
|
||||||
return false, "Invalid number."
|
|
||||||
end
|
|
||||||
return true, num
|
|
||||||
elseif argSpec.type == "bool" then
|
|
||||||
local txt = string.lower(arg)
|
|
||||||
if txt == "true" or txt == "t" or txt == "yes" or txt == "y" then
|
|
||||||
return true, true
|
|
||||||
else
|
|
||||||
return true, false
|
|
||||||
end
|
|
||||||
else
|
|
||||||
return false, "Unknown type: " .. argSpec.type
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Parses arguments according to a specification table, for common building
|
|
||||||
-- scripts, and returns a table with key-value pairs for each arg.
|
|
||||||
-- The specification table should be formatted like so:
|
|
||||||
-- {
|
|
||||||
-- argName = { type = "string", required = true, idx = 1 },
|
|
||||||
-- namedArg = { name = "-f", required = true, type = "bool" }
|
|
||||||
-- }
|
|
||||||
-- Supported types: string, number, bool
|
|
||||||
function buildscript.parseArgs(args, spec)
|
|
||||||
for name, argSpec in pairs(spec) do
|
|
||||||
if argSpec.idx ~= nil then
|
|
||||||
if type(argSpec.idx) ~= "number" or argSpec.idx < 1 then
|
|
||||||
return false, "Invalid argument specification: " .. name .. " does not have a valid numeric index."
|
|
||||||
end
|
|
||||||
elseif argSpec.name ~= nil then
|
|
||||||
if type(argSpec.name) ~= "string" or #argSpec.name < 3 then
|
|
||||||
return false, "Invalid argument specification: " .. name .. " does not have a valid string name."
|
|
||||||
end
|
|
||||||
else
|
|
||||||
return false, "Invalid argument specification: " .. name .. " doesn't have idx or name."
|
|
||||||
end
|
|
||||||
if not argSpec.type then argSpec.type = "string" end
|
|
||||||
end
|
|
||||||
|
|
||||||
local results = {}
|
|
||||||
|
|
||||||
-- Iterate over each argument specification, and try and find a value for it.
|
|
||||||
for name, argSpec in pairs(spec) do
|
|
||||||
if argSpec.idx then
|
|
||||||
-- Parse a positional argument.
|
|
||||||
if argSpec.idx > #args and argSpec.required then
|
|
||||||
return false, "Missing required positional argument " .. name .. " at index " .. argSpec.idx
|
|
||||||
end
|
|
||||||
if argSpec.idx > #args then
|
|
||||||
results[name] = nil
|
|
||||||
else
|
|
||||||
local success, value = parseArgValue(argSpec, args[argSpec.idx])
|
|
||||||
if not success then
|
|
||||||
return false, "Failed to parse value for argument " .. name .. ": " .. value
|
|
||||||
end
|
|
||||||
results[name] = value
|
|
||||||
end
|
|
||||||
else
|
|
||||||
-- Parse a named argument by iterating over all args until we find one matching the name.
|
|
||||||
local valueFound = false
|
|
||||||
for idx, arg in pairs(args) do
|
|
||||||
if arg == argSpec.name then
|
|
||||||
if idx >= #args and argSpec.required then
|
|
||||||
return false, "Missing value for required argument " .. name
|
|
||||||
end
|
|
||||||
local success, value = parseArgValue(argSpec, args[idx + 1])
|
|
||||||
if not success then
|
|
||||||
return false, "Failed to parse value for argument " .. name .. ": " .. value
|
|
||||||
end
|
|
||||||
results[name] = value
|
|
||||||
valueFound = true
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if argSpec.required and not valueFound then
|
|
||||||
return false, "Missing argument: " .. name
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return true, results
|
|
||||||
end
|
|
||||||
|
|
||||||
return buildscript
|
|
|
@ -5,362 +5,164 @@ Author: Andrew Lalis <andrewlalisofficial@gmail.com>
|
||||||
|
|
||||||
|
|
||||||
]]--
|
]]--
|
||||||
|
VERSION = "0.0.1"
|
||||||
|
|
||||||
|
local t = turtle
|
||||||
|
|
||||||
-- The itemscript module. Functions defined within this table are exported.
|
-- The itemscript module. Functions defined within this table are exported.
|
||||||
local itemscript = {}
|
local itemscript = {}
|
||||||
itemscript.VERSION = "0.0.1"
|
|
||||||
|
|
||||||
-- Determines if an item stack matches the given name.
|
-- Determines if an item stack matches the given name.
|
||||||
-- If fuzzy, then the item name will be matched against the given name.
|
-- If fuzzy, then the item name will be matched against the given name.
|
||||||
local function stackMatches(itemStack, name, fuzzy)
|
local function stackMatches(itemStack, name, fuzzy)
|
||||||
if itemStack == nil or itemStack.name == nil then return false end
|
return itemStack ~= nil and
|
||||||
if fuzzy then return string.find(itemStack.name, name) ~= nil end
|
(
|
||||||
return itemStack.name == name
|
(not fuzzy and itemStack.name == name) or
|
||||||
|
string.find(itemStack.name, name)
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function splitString(str, sep)
|
local function notFilter(filter)
|
||||||
if sep == nil then sep = "%s" end
|
return function(item)
|
||||||
local result = {}
|
return not filter(item)
|
||||||
for s in string.gmatch(str, "([^"..sep.."]+)") do
|
|
||||||
table.insert(result, s)
|
|
||||||
end
|
end
|
||||||
return result
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Parses a filter expression string and returns a table representing the syntax tree.
|
local function andFilter(filters)
|
||||||
-- An error is thrown if compilation fails.
|
return function(item)
|
||||||
|
for _, filter in pairs(filters) do
|
||||||
|
if not filter(item) then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function orFilter(filters)
|
||||||
|
return function(item)
|
||||||
|
for _, filter in pairs(filters) do
|
||||||
|
if filter(item) then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Parses a filter expression string and returns a filter that implements it.
|
||||||
--[[
|
--[[
|
||||||
Item Filter Expressions:
|
Item Filter Expressions:
|
||||||
|
|
||||||
A filter expression is a way to define a complex method of matching item
|
A filter expression is a way to define a complex method of matching item
|
||||||
stacks.
|
stacks.
|
||||||
|
|
||||||
Grammar:
|
Prepending ! will match any item stack whose name does not match.
|
||||||
|
Prepending # will do a fuzzy match using string.find.
|
||||||
word = %a[%w%-_:]* A whole or substring of an item's name.
|
|
||||||
number = %d+
|
|
||||||
expr = word Matches item stacks whose name matches the given word.
|
|
||||||
= #word Matches item stacks whose name contains the given word.
|
|
||||||
= (expr)
|
|
||||||
= !expr Matches item stacks that don't match the given expression.
|
|
||||||
= expr | expr Matches item stacks that match any of the given expressions (OR).
|
|
||||||
= expr & expr Matches item stacks that match all of the given expressions (AND).
|
|
||||||
= expr > %d Matches item stacks that match the given expression, and have more than N items.
|
|
||||||
= expr >= %d Matches item stacks that match the given expression, and have more than or equal to N items.
|
|
||||||
= expr < %d Matches item stacks that match the given expression, and have less than N items.
|
|
||||||
= expr <= %d Matches item stacks that match the given expression, and have less than or equal to N items.
|
|
||||||
= expr = %d Matches item stacks that match the given expression, and have exactly N items.
|
|
||||||
= expr != %d Matches item stacks that match the given expression, and do not have exactly N items.
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
|
|
||||||
"#log > 10" matches any items containing the word "log", that have more than 10 items in the stack.
|
|
||||||
"10% coal, 90% iron_ore" matches coal 10% of the time, and iron_ore 90% of the time.
|
|
||||||
]]--
|
]]--
|
||||||
function itemscript.parseFilterExpression(str)
|
local function parseItemFilterExpression(expr)
|
||||||
str = str:gsub("^%s*(.-)%s*$", "%1") -- Trim whitespace from the beginning and end of the string.
|
local prefixIdx, prefixIdxEnd = string.find(expr, "^[!#]+")
|
||||||
|
|
||||||
-- Parse group constructs
|
|
||||||
local ignoreRange = nil
|
|
||||||
if string.sub(str, 1, 1) == "(" then
|
|
||||||
local idx1, idx2 = string.find(str, "%b()")
|
|
||||||
if idx1 == nil then
|
|
||||||
error("Invalid group construct: \"" .. str .. "\".")
|
|
||||||
end
|
|
||||||
-- If the group is the whole expression, parse it. Otherwise, defer parsing to later.
|
|
||||||
if idx2 == #str then
|
|
||||||
return itemscript.parseFilterExpression(string.sub(str, idx1 + 1, idx2 - 1))
|
|
||||||
else
|
|
||||||
ignoreRange = {idx1, idx2}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Parse logical group operators (OR and AND)
|
|
||||||
local logicalGroupOperators = {
|
|
||||||
{ name = "OR", token = "|" },
|
|
||||||
{ name = "AND", token = "&" }
|
|
||||||
}
|
|
||||||
for _, operator in pairs(logicalGroupOperators) do
|
|
||||||
local idx = string.find(str, operator.token)
|
|
||||||
if idx ~= nil and (ignoreRange == nil or idx < ignoreRange[1] or idx > ignoreRange[2]) then
|
|
||||||
return {
|
|
||||||
type = operator.name,
|
|
||||||
children = {
|
|
||||||
itemscript.parseFilterExpression(string.sub(str, 1, idx - 1)),
|
|
||||||
itemscript.parseFilterExpression(string.sub(str, idx + 1, -1))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Parse item count arithmetic operators
|
|
||||||
local arithmeticOperators = {
|
|
||||||
["LESS_THAN"] = "<",
|
|
||||||
["LESS_THAN_OR_EQUAL_TO"] = "<=",
|
|
||||||
["GREATER_THAN"] = ">",
|
|
||||||
["GREATER_THAN_OR_EQUAL_TO"] = ">=",
|
|
||||||
["EQUALS"] = "=",
|
|
||||||
["NOT_EQUALS"] = "!="
|
|
||||||
}
|
|
||||||
for typeName, token in pairs(arithmeticOperators) do
|
|
||||||
local idx = string.find(str, token)
|
|
||||||
if idx ~= nil and (ignoreRange == nil or idx < ignoreRange[1] or idx > ignoreRange[2]) then
|
|
||||||
local subExpr = itemscript.parseFilterExpression(string.sub(str, 1, idx - 1))
|
|
||||||
local numberExprIdx1, numberExprIdx2 = string.find(str, "%d+", idx + 1)
|
|
||||||
if numberExprIdx1 == nil then
|
|
||||||
error("Could not find number expression (%d+) in string: \"" .. string.sub(str, idx + 1, -1) .. "\".")
|
|
||||||
end
|
|
||||||
local numberValue = tonumber(string.sub(str, numberExprIdx1, numberExprIdx2))
|
|
||||||
if numberValue == nil then
|
|
||||||
error("Could not parse number from string: \"" .. string.sub(str, numberExprIdx1, numberExprIdx2) .. "\".")
|
|
||||||
end
|
|
||||||
return {
|
|
||||||
type = typeName,
|
|
||||||
expr = subExpr,
|
|
||||||
value = numberValue
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Parse NOT operator.
|
|
||||||
if string.sub(str, 1, 1) == "!" then
|
|
||||||
return {
|
|
||||||
type = "NOT",
|
|
||||||
expr = itemscript.parseFilterExpression(string.sub(str, 2, -1))
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Parse fuzzy and plain words.
|
|
||||||
local fuzzy = false
|
local fuzzy = false
|
||||||
if string.sub(str, 1, 1) == "#" then
|
local negated = false
|
||||||
|
if prefixIdx ~= nil then
|
||||||
|
for i = prefixIdx, prefixIdxEnd do
|
||||||
|
local char = string.sub(expr, i, i)
|
||||||
|
if char == "!" then
|
||||||
|
negated = true
|
||||||
|
elseif char == "#" then
|
||||||
fuzzy = true
|
fuzzy = true
|
||||||
str = string.sub(str, 2, -1)
|
|
||||||
end
|
end
|
||||||
local wordIdx1, wordIdx2 = string.find(str, "%a[%w%-_:]*")
|
|
||||||
if wordIdx1 ~= nil then
|
|
||||||
local value = string.sub(str, wordIdx1, wordIdx2)
|
|
||||||
if not fuzzy and string.find(value, ":") == nil then
|
|
||||||
value = "minecraft:" .. value
|
|
||||||
end
|
end
|
||||||
return {
|
expr = string.sub(expr, prefixIdxEnd + 1, string.len(expr))
|
||||||
type = "WORD",
|
end
|
||||||
value = value,
|
local namespaceSeparatorIdx = string.find(expr, ":")
|
||||||
fuzzy = fuzzy
|
if namespaceSeparatorIdx == nil and not fuzzy then
|
||||||
}
|
expr = "minecraft:" .. expr
|
||||||
end
|
end
|
||||||
|
|
||||||
error("Invalid filter expression syntax: " .. str)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Compiles a filter function from a filter expression syntax tree.
|
|
||||||
function itemscript.compileFilter(expr)
|
|
||||||
if expr.type == "WORD" then
|
|
||||||
return function(item)
|
return function(item)
|
||||||
return stackMatches(item, expr.value, expr.fuzzy)
|
if item == nil then return false end
|
||||||
|
local matches = stackMatches(item, expr, fuzzy)
|
||||||
|
if negated then
|
||||||
|
matches = not matches
|
||||||
end
|
end
|
||||||
|
return matches
|
||||||
end
|
end
|
||||||
if expr.type == "NOT" then
|
|
||||||
local subFilter = itemscript.compileFilter(expr.expr)
|
|
||||||
return function (item)
|
|
||||||
return not subFilter(item)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if expr.type == "LESS_THAN" then
|
|
||||||
local subFilter = itemscript.compileFilter(expr.expr)
|
|
||||||
return function (item)
|
|
||||||
return subFilter(item) and item.count < expr.value
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if expr.type == "GREATER_THAN" then
|
|
||||||
local subFilter = itemscript.compileFilter(expr.expr)
|
|
||||||
return function (item)
|
|
||||||
return subFilter(item) and item.count > expr.value
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if expr.type == "LESS_THAN_OR_EQUAL_TO" then
|
|
||||||
local subFilter = itemscript.compileFilter(expr.expr)
|
|
||||||
return function (item)
|
|
||||||
return subFilter(item) and item.count <= expr.value
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if expr.type == "GREATER_THAN_OR_EQUAL_TO" then
|
|
||||||
local subFilter = itemscript.compileFilter(expr.expr)
|
|
||||||
return function (item)
|
|
||||||
return subFilter(item) and item.count >= expr.value
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if expr.type == "EQUALS" then
|
|
||||||
local subFilter = itemscript.compileFilter(expr.expr)
|
|
||||||
return function (item)
|
|
||||||
return subFilter(item) and item.count == expr.value
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if expr.type == "NOT_EQUALS" then
|
|
||||||
local subFilter = itemscript.compileFilter(expr.expr)
|
|
||||||
return function (item)
|
|
||||||
return subFilter(item) and item.count ~= expr.value
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if expr.type == "AND" then
|
|
||||||
local subFilters = {}
|
|
||||||
for _, childExpr in pairs(expr.children) do
|
|
||||||
table.insert(subFilters, itemscript.compileFilter(childExpr))
|
|
||||||
end
|
|
||||||
return function (item)
|
|
||||||
for _, subFilter in pairs(subFilters) do
|
|
||||||
if not subFilter(item) then return false end
|
|
||||||
end
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if expr.type == "OR" then
|
|
||||||
local subFilters = {}
|
|
||||||
for _, childExpr in pairs(expr.children) do
|
|
||||||
table.insert(subFilters, itemscript.compileFilter(childExpr))
|
|
||||||
end
|
|
||||||
return function (item)
|
|
||||||
for _, subFilter in pairs(subFilters) do
|
|
||||||
if subFilter(item) then return true end
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
error("Invalid filter expression syntax tree item: " .. expr.type)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--[[
|
-- Converts an arbitrary variable into a filter; useful for any function that's public, so users can supply any filter.
|
||||||
Converts an arbitrary value to a filter function that can be applied to item
|
-- It converts the following:
|
||||||
stacks for filtering operations. The following types are supported:
|
-- filter function tables directly.
|
||||||
- strings are parsed and compiled to filter functions.
|
-- strings and lists of strings are translated into an item names filter.
|
||||||
- functions are assumed to be filter functions that take an item stack as
|
-- Functions are added with default fuzzy and whitelist parameters.
|
||||||
a single parameter, and return true for a match, and false otherwise.
|
local function convertToFilter(var)
|
||||||
- tables are assumed to be pre-parsed filter expression syntax trees.
|
if type(var) == "table" and #var > 0 and type(var[1]) == "string" then
|
||||||
]]--
|
local filters = {}
|
||||||
function itemscript.filterize(value)
|
for _, expr in pairs(var) do
|
||||||
if type(value) == "string" then
|
table.insert(filters, parseItemFilterExpression(expr))
|
||||||
return itemscript.compileFilter(itemscript.parseFilterExpression(value))
|
end
|
||||||
elseif type(value) == "table" then
|
return orFilter(filters)
|
||||||
return itemscript.compileFilter(value)
|
elseif type(var) == "string" then
|
||||||
elseif type(value) == "function" then
|
return parseItemFilterExpression(var)
|
||||||
return value
|
elseif type(var) == "function" then
|
||||||
|
return var
|
||||||
else
|
else
|
||||||
error("Invalid filterizable value. Expected filter expression string, syntax tree table, or filter function.")
|
error("Unsupported filter type: " .. type(var))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Finds the first matching slot for the given filter expression.
|
|
||||||
function itemscript.findSlot(filterExpr)
|
|
||||||
local filter = itemscript.filterize(filterExpr)
|
|
||||||
for i = 1, 16 do
|
|
||||||
local item = turtle.getItemDetail(i)
|
|
||||||
if filter(item) then return i end
|
|
||||||
end
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Gets a list of all inventory slots that match the given filter expression.
|
|
||||||
function itemscript.findSlots(filterExpr)
|
|
||||||
local filter = itemscript.filterize(filterExpr)
|
|
||||||
local slots = {}
|
|
||||||
for i = 1, 16 do
|
|
||||||
local item = turtle.getItemDetail(i)
|
|
||||||
if filter(item) then
|
|
||||||
table.insert(slots, i)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return slots
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Gets the total number of items in the turtle's inventory that match the given expression.
|
-- Gets the total number of items in the turtle's inventory that match the given expression.
|
||||||
function itemscript.totalCount(filterExpr)
|
function itemscript.totalCount(filterExpr)
|
||||||
|
local filter = convertToFilter(filterExpr)
|
||||||
local count = 0
|
local count = 0
|
||||||
for _, slot in pairs(itemscript.findSlots(filterExpr)) do
|
for i = 1, 16 do
|
||||||
local item = turtle.getItemDetail(slot)
|
local item = t.getItemDetail(i)
|
||||||
|
if filter(item) then
|
||||||
count = count + item.count
|
count = count + item.count
|
||||||
end
|
end
|
||||||
|
end
|
||||||
return count
|
return count
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Select the first slot containing a matching item stack for a filter.
|
-- Selects a slot containing at least one of the given item type.
|
||||||
-- Returns a boolean indicating whether we could find and select the item.
|
-- Returns a boolean indicating whether we could find and select the item.
|
||||||
function itemscript.select(filterExpr)
|
function itemscript.select(filterExpr)
|
||||||
local slot = itemscript.findSlot(filterExpr)
|
local filter = convertToFilter(filterExpr)
|
||||||
if slot ~= nil then
|
|
||||||
turtle.select(slot)
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Selects a random slot containing a matching item stack.
|
|
||||||
function itemscript.selectRandom(filterExpr)
|
|
||||||
local eligibleSlots = itemscript.findSlots(filterExpr)
|
|
||||||
if #eligibleSlots == 0 then return false end
|
|
||||||
local slot = eligibleSlots[math.random(1, #eligibleSlots)]
|
|
||||||
turtle.select(slot)
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Selects a slot containing at least 1 of an item type matching
|
|
||||||
-- the given filter expression.
|
|
||||||
function itemscript.selectOrWait(filterExpr)
|
|
||||||
local filter = itemscript.filterize(filterExpr)
|
|
||||||
while not itemscript.select(filter) do
|
|
||||||
print("Couldn't find at least 1 item matching the filter expression: \"" .. filterExpr .. "\". Please add it.")
|
|
||||||
os.pullEvent("turtle_inventory")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function itemscript.selectRandomOrWait(filterExpr)
|
|
||||||
local filter = itemscript.filterize(filterExpr)
|
|
||||||
while not itemscript.select(filter) do
|
|
||||||
print("Couldn't find at least 1 item matching the filter expression: \"" .. filterExpr .. "\". Please add it.")
|
|
||||||
os.pullEvent("turtle_inventory")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Selects the first empty slot, if there is one. Returns true if an empty slot could be selected.
|
|
||||||
function itemscript.selectEmpty()
|
|
||||||
for i = 1, 16 do
|
for i = 1, 16 do
|
||||||
local item = turtle.getItemDetail(i)
|
local item = t.getItemDetail(i)
|
||||||
if item == nil then
|
if filter(item) then
|
||||||
turtle.select(i)
|
t.select(i)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Selects the first empty slot, or prompts the user to remove items so that an empty slot can be selected.
|
|
||||||
function itemscript.selectEmptyOrWait()
|
|
||||||
while not itemscript.selectEmpty() do
|
|
||||||
print("Couldn't find an empty slot. Please remove some items.")
|
|
||||||
os.pullEvent("turtle_inventory")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Helper function to drop items in a flexible way, using a drop function and filtering function.
|
-- Helper function to drop items in a flexible way, using a drop function and filtering function.
|
||||||
local function dropFiltered(dropFunction, filterExpr)
|
local function dropFiltered(dropFunction, filter)
|
||||||
local filter = itemscript.filterize(filterExpr)
|
|
||||||
for i = 1, 16 do
|
for i = 1, 16 do
|
||||||
local item = turtle.getItemDetail(i)
|
local item = t.getItemDetail(i)
|
||||||
if filter(item) then
|
if filter(item) then
|
||||||
turtle.select(i)
|
t.select(i)
|
||||||
dropFunction()
|
dropFunction()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function itemscript.dropAll(filterExpr)
|
function itemscript.dropAll(filterExpr)
|
||||||
dropFiltered(turtle.drop, filterExpr)
|
dropFiltered(t.drop, convertToFilter(filterExpr))
|
||||||
end
|
end
|
||||||
|
|
||||||
function itemscript.dropAllDown(filterExpr)
|
function itemscript.dropAllDown(filterExpr)
|
||||||
dropFiltered(turtle.dropDown, filterExpr)
|
dropFiltered(t.dropDown, convertToFilter(filterExpr))
|
||||||
end
|
end
|
||||||
|
|
||||||
function itemscript.dropAllUp(filterExpr)
|
function itemscript.dropAllUp(filterExpr)
|
||||||
dropFiltered(turtle.dropUp, filterExpr)
|
dropFiltered(t.dropUp, convertToFilter(filterExpr))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Cleans up the turtle's inventory by compacting all stacks of items.
|
||||||
|
function itemscript.organize()
|
||||||
|
error("Not yet implemented.")
|
||||||
end
|
end
|
||||||
|
|
||||||
return itemscript
|
return itemscript
|
|
@ -7,15 +7,12 @@ Movescript provides a simpler, conciser way to program "turtles" (robots), so
|
||||||
that you don't need to get tired of typing "turtle.forward()" over and over.
|
that you don't need to get tired of typing "turtle.forward()" over and over.
|
||||||
|
|
||||||
]]--
|
]]--
|
||||||
|
VERSION = "0.0.1"
|
||||||
|
|
||||||
local t = turtle
|
local t = turtle
|
||||||
-- For testing purposes, if the turtle API is not present, we inject our own.
|
|
||||||
if not t then t = {
|
|
||||||
getFuelLimit = function() return 1000000000 end
|
|
||||||
} end
|
|
||||||
|
|
||||||
-- The movescript module. Functions defined within this table are exported.
|
-- The movescript module. Functions defined within this table are exported.
|
||||||
local movescript = {}
|
local movescript = {}
|
||||||
movescript.VERSION = "0.0.1"
|
|
||||||
|
|
||||||
movescript.defaultSettings = {
|
movescript.defaultSettings = {
|
||||||
debug = false,
|
debug = false,
|
||||||
|
@ -24,11 +21,6 @@ movescript.defaultSettings = {
|
||||||
fuels = {"minecraft:coal", "minecraft:charcoal"}
|
fuels = {"minecraft:coal", "minecraft:charcoal"}
|
||||||
}
|
}
|
||||||
|
|
||||||
local INSTRUCTION_TYPES = {
|
|
||||||
repeated = 1,
|
|
||||||
instruction = 2
|
|
||||||
}
|
|
||||||
|
|
||||||
local function debug(msg, settings)
|
local function debug(msg, settings)
|
||||||
if settings and settings.debug then
|
if settings and settings.debug then
|
||||||
print("[MS] " .. msg)
|
print("[MS] " .. msg)
|
||||||
|
@ -70,160 +62,36 @@ local function goDirection(dirFunction, digFunction, detectFunction, settings)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function goUp(options, settings)
|
local function goUp(settings)
|
||||||
debug("Moving up.", settings)
|
debug("Moving up.", settings)
|
||||||
goDirection(t.up, t.digUp, t.detectUp, settings)
|
goDirection(t.up, t.digUp, t.detectUp, settings)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function goDown(options, settings)
|
local function goDown(settings)
|
||||||
debug("Moving down.", settings)
|
debug("Moving down.", settings)
|
||||||
goDirection(t.down, t.digDown, t.detectDown, settings)
|
goDirection(t.down, t.digDown, t.detectDown, settings)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function goForward(options, settings)
|
local function goForward(settings)
|
||||||
debug("Moving forward.", settings)
|
debug("Moving forward.", settings)
|
||||||
goDirection(t.forward, t.dig, t.detect, settings)
|
goDirection(t.forward, t.dig, t.detect, settings)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function goBack(options, settings)
|
local function goBack(settings)
|
||||||
debug("Moving back.", settings)
|
debug("Moving back.", settings)
|
||||||
goDirection(t.back, t.digBack, t.detectBack, settings)
|
goDirection(t.back, t.digBack, t.detectBack, settings)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function goRight(options, settings)
|
local function goRight(settings)
|
||||||
debug("Turning right.", settings)
|
debug("Turning right.", settings)
|
||||||
t.turnRight()
|
t.turnRight()
|
||||||
end
|
end
|
||||||
|
|
||||||
local function goLeft(options, settings)
|
local function goLeft(settings)
|
||||||
debug("Turning left.", settings)
|
debug("Turning left.", settings)
|
||||||
t.turnLeft()
|
t.turnLeft()
|
||||||
end
|
end
|
||||||
|
|
||||||
local function placeFunction(func, digFunction, detectFunction, settings, options)
|
|
||||||
settings = settings or movescript.defaultSettings
|
|
||||||
safe = settings.safe or movescript.defaultSettings.safe
|
|
||||||
destructive = settings.destructive or movescript.defaultSettings.destructive
|
|
||||||
local success = func(options.text)
|
|
||||||
if not safe then return end
|
|
||||||
while not success do
|
|
||||||
debug("Unable to place.", settings)
|
|
||||||
if destructive and detectFunction() then
|
|
||||||
debug("Detected a block in the way; attempting to remove it.", settings)
|
|
||||||
digFunction()
|
|
||||||
end
|
|
||||||
success = func(options.text)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function place(options, settings)
|
|
||||||
debug("Placing.", settings)
|
|
||||||
placeFunction(t.place, t.dig, t.detect, settings, options)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function placeUp(options, settings)
|
|
||||||
debug("Placing up.", settings)
|
|
||||||
placeFunction(t.placeUp, t.digUp, t.detectUp, settings, options)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function placeDown(options, settings)
|
|
||||||
debug("Placing down.", settings)
|
|
||||||
placeFunction(t.placeDown, t.digDown, t.detectDown, settings, options)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function attack(options, settings)
|
|
||||||
debug("Attacking.", settings)
|
|
||||||
t.attack(options.side)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function attackUp(options, settings)
|
|
||||||
debug("Attacking up.", settings)
|
|
||||||
t.attackUp(options.side)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function attackDown(options, settings)
|
|
||||||
debug("Attacking down.", settings)
|
|
||||||
t.attackDown(options.side)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function dig(options, settings)
|
|
||||||
debug("Digging.", settings)
|
|
||||||
t.dig(options.side)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function digUp(options, settings)
|
|
||||||
debug("Digging up.", settings)
|
|
||||||
t.digUp(options.side)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function digDown(options, settings)
|
|
||||||
debug("Digging down.", settings)
|
|
||||||
t.digDown(options.side)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function suck(options, settings)
|
|
||||||
debug("Sucking.", settings)
|
|
||||||
local count = nil
|
|
||||||
if options.count ~= nil then
|
|
||||||
count = tonumber(options.count)
|
|
||||||
end
|
|
||||||
t.suck(count)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function suckUp(options, settings)
|
|
||||||
debug("Sucking up.", settings)
|
|
||||||
local count = nil
|
|
||||||
if options.count ~= nil then
|
|
||||||
count = tonumber(options.count)
|
|
||||||
end
|
|
||||||
t.suckUp(count)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function suckDown(options, settings)
|
|
||||||
debug("Sucking down.", settings)
|
|
||||||
local count = nil
|
|
||||||
if options.count ~= nil then
|
|
||||||
count = tonumber(options.count)
|
|
||||||
end
|
|
||||||
t.suckDown(count)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function selectSlot(options, settings)
|
|
||||||
local slot = 1
|
|
||||||
if options.slot ~= nil then
|
|
||||||
slot = tonumber(options.slot)
|
|
||||||
end
|
|
||||||
debug("Selecting slot " .. slot .. ".", settings)
|
|
||||||
t.select(slot)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function drop(options, settings)
|
|
||||||
debug("Dropping.", settings)
|
|
||||||
local count = nil
|
|
||||||
if options.count ~= nil then
|
|
||||||
count = tonumber(options.count)
|
|
||||||
end
|
|
||||||
t.drop(count)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function dropUp(options, settings)
|
|
||||||
debug("Dropping up.", settings)
|
|
||||||
local count = nil
|
|
||||||
if options.count ~= nil then
|
|
||||||
count = tonumber(options.count)
|
|
||||||
end
|
|
||||||
t.dropUp(count)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function dropDown(options, settings)
|
|
||||||
debug("Dropping down.", settings)
|
|
||||||
local count = nil
|
|
||||||
if options.count ~= nil then
|
|
||||||
count = tonumber(options.count)
|
|
||||||
end
|
|
||||||
t.dropDown(count)
|
|
||||||
end
|
|
||||||
|
|
||||||
local actionMap = {
|
local actionMap = {
|
||||||
["U"] = {f = goUp, needsFuel = true},
|
["U"] = {f = goUp, needsFuel = true},
|
||||||
["D"] = {f = goDown, needsFuel = true},
|
["D"] = {f = goDown, needsFuel = true},
|
||||||
|
@ -231,24 +99,12 @@ local actionMap = {
|
||||||
["R"] = {f = goRight, needsFuel = false},
|
["R"] = {f = goRight, needsFuel = false},
|
||||||
["F"] = {f = goForward, needsFuel = true},
|
["F"] = {f = goForward, needsFuel = true},
|
||||||
["B"] = {f = goBack, needsFuel = true},
|
["B"] = {f = goBack, needsFuel = true},
|
||||||
["P"] = {f = place, needsFuel = false},
|
["P"] = {f = t.place, needsFuel = false},
|
||||||
["Pu"] = {f = placeUp, needsFuel = false},
|
["Pu"] = {f = t.placeUp, needsFuel = false},
|
||||||
["Pd"] = {f = placeDown, needsFuel = false},
|
["Pd"] = {f = t.placeDown, needsFuel = false},
|
||||||
["A"] = {f = attack, needsFuel = false},
|
["A"] = {f = t.attack, needsFuel = false},
|
||||||
["Au"] = {f = attackUp, needsFuel = false},
|
["Au"] = {f = t.attackUp, needsFuel = false},
|
||||||
["Ad"] = {f = attackDown, needsFuel = false},
|
["Ad"] = {f = t.attackDown, needsFuel = false}
|
||||||
["Dg"] = {f = dig, needsFuel = false},
|
|
||||||
["Dgu"] = {f = digUp, needsFuel = false},
|
|
||||||
["Dgd"] = {f = digDown, needsFuel = false},
|
|
||||||
["S"] = {f = suck, needsFuel = false},
|
|
||||||
["Su"] = {f = suckUp, needsFuel = false},
|
|
||||||
["Sd"] = {f = suckDown, needsFuel = false},
|
|
||||||
["Eqr"] = {f = t.equipRight, needsFuel = false},
|
|
||||||
["Eql"] = {f = t.equipLeft, needsFuel = false},
|
|
||||||
["Sel"] = {f = selectSlot, needsFuel = false},
|
|
||||||
["Dr"] = {f = drop, needsFuel = false},
|
|
||||||
["Dru"] = {f = dropUp, needsFuel = false},
|
|
||||||
["Drd"] = {f = dropDown, needsFuel = false}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
-- Tries to refuel the turtle from all slots that contain a valid fuel.
|
-- Tries to refuel the turtle from all slots that contain a valid fuel.
|
||||||
|
@ -262,7 +118,7 @@ local function refuelAll(settings)
|
||||||
if item ~= nil then
|
if item ~= nil then
|
||||||
for _, fuelName in pairs(fuels) do
|
for _, fuelName in pairs(fuels) do
|
||||||
if item.name == fuelName then
|
if item.name == fuelName then
|
||||||
t.select(slot)
|
t.select(i)
|
||||||
if t.refuel(item.count) then refueled = true end
|
if t.refuel(item.count) then refueled = true end
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
@ -275,7 +131,7 @@ end
|
||||||
-- Blocks until the turtle's fuel level is at least at the required level.
|
-- Blocks until the turtle's fuel level is at least at the required level.
|
||||||
local function refuelToAtLeast(requiredLevel, settings)
|
local function refuelToAtLeast(requiredLevel, settings)
|
||||||
refuelAll(settings)
|
refuelAll(settings)
|
||||||
while t.getFuelLevel() < requiredLevel do
|
while t.getFuelLevel < requiredLevel do
|
||||||
print(
|
print(
|
||||||
"[MS] Fuel level is too low. Level: " .. t.getFuelLevel() .. ". Required: " .. requiredLevel ..
|
"[MS] Fuel level is too low. Level: " .. t.getFuelLevel() .. ". Required: " .. requiredLevel ..
|
||||||
". Please add some of the following fuels:"
|
". Please add some of the following fuels:"
|
||||||
|
@ -287,28 +143,19 @@ local function refuelToAtLeast(requiredLevel, settings)
|
||||||
local fuelUpdated = false
|
local fuelUpdated = false
|
||||||
while not fuelUpdated do
|
while not fuelUpdated do
|
||||||
os.pullEvent("turtle_inventory")
|
os.pullEvent("turtle_inventory")
|
||||||
fuelUpdated = refuelAll(settings)
|
fuelUpdated = refuelAll()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Executes a single instruction. An instruction is a table with an "action"
|
-- Executes a single instruction. An instruction is a table with an "action"
|
||||||
-- and some attributes, such as if it needs fuel or not.
|
-- and some attributes, such as if it needs fuel or not.
|
||||||
function movescript.executeInstruction(instruction, settings, preExecuteHook, postExecuteHook)
|
local function executeInstruction(instruction, settings)
|
||||||
if settings == nil then settings = movescript.defaultSettings end
|
|
||||||
if instruction.type == INSTRUCTION_TYPES.repeated then
|
|
||||||
debug("Executing repeated instruction " .. instruction.count .. " times.", settings)
|
|
||||||
for i = 1, instruction.count do
|
|
||||||
for _, nestedInstruction in pairs(instruction.instructions) do
|
|
||||||
movescript.executeInstruction(nestedInstruction, settings, preExecuteHook, postExecuteHook)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
elseif instruction.type == INSTRUCTION_TYPES.instruction then
|
|
||||||
local action = actionMap[instruction.action]
|
local action = actionMap[instruction.action]
|
||||||
if action then
|
if action then
|
||||||
debug("Executing action \"" .. instruction.action .. "\" " .. instruction.count .. " times.", settings)
|
debug("Executing action \"" .. instruction.action .. "\" " .. instruction.count .. " times.", settings)
|
||||||
local shouldRefuel = (
|
local shouldRefuel = (
|
||||||
((settings ~= nil and settings.safe) or true) and
|
(settings.safe or true) and
|
||||||
(action.needsFuel) and
|
(action.needsFuel) and
|
||||||
(instruction.count > t.getFuelLevel())
|
(instruction.count > t.getFuelLevel())
|
||||||
)
|
)
|
||||||
|
@ -316,158 +163,48 @@ function movescript.executeInstruction(instruction, settings, preExecuteHook, po
|
||||||
local fuelRequired = instruction.count
|
local fuelRequired = instruction.count
|
||||||
refuelToAtLeast(fuelRequired, settings)
|
refuelToAtLeast(fuelRequired, settings)
|
||||||
end
|
end
|
||||||
for i = 1, instruction.count do
|
for i = 1, instruction.count do action.f() end
|
||||||
if preExecuteHook ~= nil then preExecuteHook() end
|
|
||||||
action.f(instruction.options, settings)
|
|
||||||
if postExecuteHook ~= nil then postExecuteHook() end
|
|
||||||
end
|
end
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function parseInstructionOptions(text, settings)
|
|
||||||
local idx, endIdx = string.find(text, "%b()")
|
|
||||||
if idx == nil or endIdx - idx < 4 then return nil end
|
|
||||||
local optionPairsText = string.sub(text, idx, endIdx)
|
|
||||||
debug("Parsing instruction options: " .. optionPairsText, settings)
|
|
||||||
local options = {}
|
|
||||||
local nextIdx = 1
|
|
||||||
while nextIdx < string.len(optionPairsText) do
|
|
||||||
idx, endIdx = string.find(optionPairsText, "%w+=[%w_-%.]+", nextIdx)
|
|
||||||
if idx == nil then break end
|
|
||||||
local pairText = string.sub(optionPairsText, idx, endIdx)
|
|
||||||
local keyIdx, keyEndIdx = string.find(pairText, "%w+")
|
|
||||||
local key = string.sub(pairText, keyIdx, keyEndIdx)
|
|
||||||
local valueIdx, valueEndIdx = string.find(pairText, "[%w_-%.]+", keyEndIdx + 2)
|
|
||||||
local value = string.sub(pairText, valueIdx, valueEndIdx)
|
|
||||||
options[key] = value
|
|
||||||
debug(" Found option: key = " .. key .. ", value = " .. value, settings)
|
|
||||||
nextIdx = endIdx + 2
|
|
||||||
end
|
|
||||||
return options
|
|
||||||
end
|
|
||||||
|
|
||||||
local function parseRepeatedInstruction(match, settings)
|
|
||||||
debug("Parsing repeated instruction: " .. match, settings)
|
|
||||||
local instruction = {}
|
|
||||||
instruction.type = INSTRUCTION_TYPES.repeated
|
|
||||||
local countIdx, countEndIdx = string.find(match, "%d+")
|
|
||||||
instruction.count = tonumber(string.sub(match, countIdx, countEndIdx))
|
|
||||||
if instruction.count < 0 then
|
|
||||||
error("Repeated instruction cannot have a negative count.")
|
|
||||||
end
|
|
||||||
local innerScriptIdx, innerScriptEndIdx = string.find(match, "%b()", countEndIdx + 1)
|
|
||||||
local innerScript = string.sub(match, innerScriptIdx + 1, innerScriptEndIdx - 1)
|
|
||||||
instruction.instructions = movescript.parse(innerScript, settings)
|
|
||||||
return instruction
|
|
||||||
end
|
|
||||||
|
|
||||||
local function parseInstruction(match, settings)
|
|
||||||
debug("Parsing instruction: " .. match, settings)
|
|
||||||
local instruction = {}
|
|
||||||
instruction.type = INSTRUCTION_TYPES.instruction
|
|
||||||
local countIdx, countEndIdx = string.find(match, "%d+")
|
|
||||||
instruction.count = 1
|
|
||||||
if countIdx ~= nil then
|
|
||||||
instruction.count = tonumber(string.sub(match, countIdx, countEndIdx))
|
|
||||||
end
|
|
||||||
if instruction.count < 1 or instruction.count > t.getFuelLimit() then
|
|
||||||
error("Instruction at index " .. actionIdx .. " has an invalid count of " .. instruction.count .. ". It should be >= 1 and <= " .. t.getFuelLimit())
|
|
||||||
end
|
|
||||||
local actionIdx, actionEndIdx = string.find(match, "%u%l*")
|
|
||||||
instruction.action = string.sub(match, actionIdx, actionEndIdx)
|
|
||||||
if actionMap[instruction.action] == nil then
|
|
||||||
error("Instruction at index " .. actionIdx .. ", \"" .. instruction.action .. "\", does not refer to a valid action.")
|
|
||||||
end
|
|
||||||
return instruction
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Parses a movescript script into a series of instruction tables.
|
-- Parses a movescript script into a series of instruction tables.
|
||||||
--[[
|
local function parseScript(script, settings)
|
||||||
Movescript Grammar:
|
|
||||||
block: instruction | repeatedInstructions
|
|
||||||
|
|
||||||
repeatedInstructions: count '(' {instruction | repeatedInstructions} ')'
|
|
||||||
regex: %d+%s*%b()
|
|
||||||
instruction: [count] action [actionOptions] <- Not yet implemented.
|
|
||||||
regex: %d*%u%l*
|
|
||||||
count: %d+
|
|
||||||
|
|
||||||
action: %u%l*
|
|
||||||
|
|
||||||
actionOptions: '(' {optionPair ','} ')'
|
|
||||||
regex: %b()
|
|
||||||
|
|
||||||
optionPair: optionKey '=' optionValue
|
|
||||||
|
|
||||||
optionKey: %w+
|
|
||||||
|
|
||||||
optionValue: [%w_-]+
|
|
||||||
|
|
||||||
]]--
|
|
||||||
function movescript.parse(script, settings)
|
|
||||||
local instructions = {}
|
local instructions = {}
|
||||||
local scriptIdx = 1
|
for instruction in string.gfind(script, "%W*(%d*%u%l*)%W*") do
|
||||||
while scriptIdx <= string.len(script) do
|
local countIdx, countIdxEnd = string.find(instruction, "%d+")
|
||||||
local instruction = {}
|
local actionIdx, actionIdxEnd = string.find(instruction, "%u%l*")
|
||||||
local repeatedMatchStartIdx, repeatedMatchEndIdx = string.find(script, "%d+%s*%b()", scriptIdx)
|
local count = 1
|
||||||
local instructionMatchStartIdx, instructionMatchEndIdx = string.find(script, "%d*%u%l*", scriptIdx)
|
if countIdx ~= nil then
|
||||||
-- Parse the first occurring matched pattern.
|
count = tonumber(string.sub(instruction, countIdx, countIdxEnd))
|
||||||
if repeatedMatchStartIdx ~= nil and (instructionMatchStartIdx == nil or repeatedMatchStartIdx < instructionMatchStartIdx) then
|
|
||||||
-- Parse repeated instructions.
|
|
||||||
local match = string.sub(script, repeatedMatchStartIdx, repeatedMatchEndIdx)
|
|
||||||
table.insert(instructions, parseRepeatedInstruction(match, settings))
|
|
||||||
scriptIdx = repeatedMatchEndIdx + 1
|
|
||||||
elseif instructionMatchStartIdx ~= nil and (repeatedMatchStartIdx == nil or instructionMatchStartIdx < repeatedMatchStartIdx) then
|
|
||||||
-- Parse single instruction.
|
|
||||||
local match = string.sub(script, instructionMatchStartIdx, instructionMatchEndIdx)
|
|
||||||
local instruction = parseInstruction(match, settings)
|
|
||||||
local optionsIdx, optionsEndIdx = string.find(script, "%s*%b()", instructionMatchEndIdx + 1)
|
|
||||||
if optionsIdx ~= nil then
|
|
||||||
-- Check that there's nothing but empty space between the instruction and the options text.
|
|
||||||
if not string.find(string.sub(script, instructionMatchEndIdx + 1, optionsIdx - 1), "%S+") then
|
|
||||||
local optionsText = string.sub(script, optionsIdx, optionsEndIdx)
|
|
||||||
instruction.options = parseInstructionOptions(optionsText, settings)
|
|
||||||
end
|
end
|
||||||
|
local action = string.sub(instruction, actionIdx, actionIdxEnd)
|
||||||
|
if count < 1 or count > t.getFuelLimit() then
|
||||||
|
error("Instruction at index " .. actionIdx .. " has an invalid count of " .. count .. ". It should be >= 1 and <= " .. t.getFuelLimit())
|
||||||
end
|
end
|
||||||
if instruction.options == nil then instruction.options = {} end
|
if actionMap[action] == nil then
|
||||||
table.insert(instructions, instruction)
|
error("Instruction at index " .. actionIdx .. ", \"" .. action .. "\", does not refer to a valid action.")
|
||||||
scriptIdx = instructionMatchEndIdx + 1
|
|
||||||
else
|
|
||||||
error("Invalid script characters found at index " .. scriptIdx)
|
|
||||||
end
|
end
|
||||||
|
table.insert(instructions, {action = action, count = count})
|
||||||
|
debug("Parsed instruction: " .. instruction, settings)
|
||||||
end
|
end
|
||||||
return instructions
|
return instructions
|
||||||
end
|
end
|
||||||
|
|
||||||
function movescript.run(script, settings, preExecuteHook, postExecuteHook)
|
function movescript.run(script, settings)
|
||||||
settings = settings or movescript.defaultSettings
|
settings = settings or movescript.defaultSettings
|
||||||
script = script or ""
|
script = script or ""
|
||||||
debug("Executing script: " .. script, settings)
|
debug("Executing script: " .. script, settings)
|
||||||
local instructions = movescript.parse(script, settings)
|
local instructions = parseScript(script, settings)
|
||||||
for idx, instruction in pairs(instructions) do
|
for idx, instruction in pairs(instructions) do
|
||||||
movescript.executeInstruction(instruction, settings, preExecuteHook, postExecuteHook)
|
executeInstruction(instruction, settings)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function movescript.runFile(filename, settings, preExecuteHook, postExecuteHook)
|
function movescript.runFile(filename, settings)
|
||||||
local f = fs.open(filename, "r")
|
local f = fs.open(filename, "r")
|
||||||
local script = f.readAll()
|
local script = f.readAll()
|
||||||
f.close()
|
f.close()
|
||||||
movescript.run(script, settings, preExecuteHook, postExecuteHook)
|
movescript.run(script, settings)
|
||||||
end
|
|
||||||
|
|
||||||
function movescript.validate(script, settings)
|
|
||||||
return pcall(function () movescript.parse(script, settings) end)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- "Mirrors" a movescript; that is, swaps any "turn right" instructions for "turn left", and vice versa.
|
|
||||||
-- Note that it does not mirror "equip right" and "equip left" instructions.
|
|
||||||
function movescript.mirror(script)
|
|
||||||
local template = string.gsub(script, "L", "__LEFT__")
|
|
||||||
template = string.gsub(template, "R", "__RIGHT__")
|
|
||||||
local result = string.gsub(template, "__LEFT__", "R")
|
|
||||||
return string.gsub(result, "__RIGHT__", "L")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return movescript
|
return movescript
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
--[[
|
|
||||||
An installation script that manages installing all movescript libraries easily.
|
|
||||||
]]--
|
|
||||||
|
|
||||||
local libs = {
|
|
||||||
"movescript.lua",
|
|
||||||
"itemscript.lua",
|
|
||||||
"buildscript.lua"
|
|
||||||
}
|
|
||||||
|
|
||||||
local BASE_URL = "https://andrewlalis.github.io/movescript/scripts/"
|
|
||||||
|
|
||||||
print("Running Movescript installer")
|
|
||||||
print("----------------------------")
|
|
||||||
for _, lib in pairs(libs) do
|
|
||||||
if fs.exists(lib) then
|
|
||||||
fs.delete(lib)
|
|
||||||
print("Deleted " .. lib)
|
|
||||||
end
|
|
||||||
local success = shell.run("wget", BASE_URL .. lib)
|
|
||||||
if not success then
|
|
||||||
error("Failed to install " .. lib)
|
|
||||||
end
|
|
||||||
print("Downloaded " .. lib)
|
|
||||||
end
|
|
||||||
print("----------------------------")
|
|
||||||
print("Done!")
|
|
46
tester.lua
46
tester.lua
|
@ -1,46 +0,0 @@
|
||||||
-- http://lua-users.org/wiki/TableSerialization
|
|
||||||
function print_r (t, name, indent)
|
|
||||||
local tableList = {}
|
|
||||||
function table_r (t, name, indent, full)
|
|
||||||
local serial=string.len(full) == 0 and name
|
|
||||||
or type(name)~="number" and '["'..tostring(name)..'"]' or '['..name..']'
|
|
||||||
io.write(indent,serial,' = ')
|
|
||||||
if type(t) == "table" then
|
|
||||||
if tableList[t] ~= nil then io.write('{}; -- ',tableList[t],' (self reference)\n')
|
|
||||||
else
|
|
||||||
tableList[t]=full..serial
|
|
||||||
if next(t) then -- Table not empty
|
|
||||||
io.write('{\n')
|
|
||||||
for key,value in pairs(t) do table_r(value,key,indent..'\t',full..serial) end
|
|
||||||
io.write(indent,'};\n')
|
|
||||||
else io.write('{};\n') end
|
|
||||||
end
|
|
||||||
else io.write(type(t)~="number" and type(t)~="boolean" and '"'..tostring(t)..'"'
|
|
||||||
or tostring(t),';\n') end
|
|
||||||
end
|
|
||||||
table_r(t,name or '__unnamed__',indent or '','')
|
|
||||||
end
|
|
||||||
|
|
||||||
-- local ms = require("src/movescript")
|
|
||||||
-- print_r(ms.parse("35(2F(safe=false)R 3(L(delay=0.25, file=file.txt)UB))", {debug=true}))
|
|
||||||
|
|
||||||
-- local bs = require("src/buildscript")
|
|
||||||
-- local args = {...}
|
|
||||||
-- local spec = {
|
|
||||||
-- num = { type = "number", required = true, idx = 1 },
|
|
||||||
-- name = { name = "name", type = "bool", required = true }
|
|
||||||
-- }
|
|
||||||
-- local success, result = bs.parseArgs(args, spec)
|
|
||||||
-- print(success)
|
|
||||||
-- print_r(result)
|
|
||||||
|
|
||||||
local is = require("src/itemscript")
|
|
||||||
local t = is.parseFilterExpression("!log")
|
|
||||||
print_r(t, "filter_expression_syntax_tree", " ")
|
|
||||||
local filter = is.compileFilter(t)
|
|
||||||
local item = {
|
|
||||||
name = "minecraft:oak_log",
|
|
||||||
count = 54
|
|
||||||
}
|
|
||||||
local matches = filter(item)
|
|
||||||
print(matches)
|
|
Loading…
Reference in New Issue