mirror of
https://github.com/adambard/learnxinyminutes-docs.git
synced 2024-12-23 17:41:41 +00:00
423 lines
15 KiB
Markdown
423 lines
15 KiB
Markdown
|
---
|
||
|
language: MiniScript
|
||
|
contributors:
|
||
|
- ["Joe Strout", "https://github.com/JoeStrout"]
|
||
|
filename: miniscript.ms
|
||
|
---
|
||
|
|
||
|
**MiniScript** is a simple scripting language designed to be easily embedded in games and other software. It can also be used on the command line, or as a cross-platform game development environment via [Soda](https://github.com/JoeStrout/soda) or [Mini Micro](https://miniscript.org/MiniMicro).
|
||
|
|
||
|
An easy way to get started with MiniScript is on the [Try-It! page](https://miniscript.org/tryit/), which runs MiniScript code on the server. Note however that the code on this page is limited to 2000 characters. (The tutorial scripts below are broken up into blocks 2048 characters or less so they will run on the Try-It! page.)
|
||
|
|
||
|
Once you are ready to go beyond the Try-It! page, your next stop should probably be to download [Mini Micro](https://miniscript.org/MiniMicro), a free virtual computer that uses MiniScript both on the command line and in programs. In that environment, enter **edit** at the prompt to edit code, then click the Run button in the editor to run it.
|
||
|
|
||
|
```
|
||
|
print "Hello world"
|
||
|
|
||
|
// MiniScript is very syntax-light. Notice that no parentheses are
|
||
|
// needed on the print statement above. Comments begin with //, and
|
||
|
// extend to the end of the line. MiniScript is case-sensitive.
|
||
|
|
||
|
// CONTROL FLOW
|
||
|
// Use if blocks to do different things depending on some condition.
|
||
|
// Include zero or more else if blocks, and one optional else block.
|
||
|
if 2+2 == 4 then
|
||
|
print "math works!"
|
||
|
else if pi > 3 then
|
||
|
print "pi is tasty"
|
||
|
else if "a" < "b" then
|
||
|
print "I can sort"
|
||
|
else
|
||
|
print "last chance"
|
||
|
end if
|
||
|
|
||
|
// LOOPING
|
||
|
// MiniScript has only two loop constructs: while loops and for loops.
|
||
|
// Use a while block to loop as long as a condition is true.
|
||
|
s = "Spam"
|
||
|
while s.len < 50
|
||
|
s = s + ", spam"
|
||
|
end while
|
||
|
print s + " and spam!"
|
||
|
|
||
|
// A for loop can loop over any list, including ones easily created
|
||
|
// with the range function.
|
||
|
for i in range(10, 1)
|
||
|
print i + "..."
|
||
|
end for
|
||
|
print "Liftoff!"
|
||
|
|
||
|
// Two additional keywords are useful inside loops. The break statement
|
||
|
// jumps out of the nearest while or for loop. The continue statement
|
||
|
// jumps to the top of the loop, skipping the rest of the current iteration.
|
||
|
for i in range(1,100)
|
||
|
if i % 3 == 0 then continue // skip multiples of 3
|
||
|
if i^2 > 200 then break // stop when i^2 is over 200
|
||
|
print i + " squared is " + i^2
|
||
|
end for
|
||
|
```
|
||
|
|
||
|
### Numbers
|
||
|
|
||
|
```
|
||
|
// All numbers are stored in full-precision format. Numbers also
|
||
|
// represent true (1) and false (0), and there are built-in keywords
|
||
|
// (true and false) for those.
|
||
|
a = 7
|
||
|
b = 3
|
||
|
ultimateAnswer = 42
|
||
|
pi = 3.14159
|
||
|
n = true
|
||
|
m = false
|
||
|
print ultimateAnswer + ", " + pi + ", " + n + ", " + m
|
||
|
|
||
|
// Numbers support the following operators:
|
||
|
print "Basic math:"
|
||
|
print a + b // addition
|
||
|
print a - b // subtraction
|
||
|
print a * b // multiplication
|
||
|
print a / b // division
|
||
|
print a % b // modulo (remainder)
|
||
|
print a ^ b // power
|
||
|
|
||
|
print "Logic:"
|
||
|
print n and m // logical "and"
|
||
|
print n or m // logical "or"
|
||
|
print not n // logical negation
|
||
|
|
||
|
print "Comparisons:"
|
||
|
print a == b // equality test (note == rather than = here!)
|
||
|
print a != b // inequality
|
||
|
print a > b // greater than
|
||
|
print a >= b // greater than or equal
|
||
|
print a < b // less than
|
||
|
print a <= b // less than or equal
|
||
|
```
|
||
|
|
||
|
### Strings
|
||
|
|
||
|
```
|
||
|
// Text is stored in strings of Unicode characters. Write strings
|
||
|
// by surrounding them with quotes. If you need to include a
|
||
|
// quotation mark in a string, type it twice.
|
||
|
print "Hello, ""Bob""."
|
||
|
a = "Hello"
|
||
|
b = "Spam"
|
||
|
|
||
|
// Strings support the following operators:
|
||
|
print "String ""math"":"
|
||
|
print a + b // string concatenation
|
||
|
print b - "m" // string subtraction (chop)
|
||
|
print b * 4 // string replication
|
||
|
print a / 2 // string division
|
||
|
|
||
|
print "Comparisons:"
|
||
|
print a == b // equality test (note == rather than = here!)
|
||
|
print a != b // inequality
|
||
|
print a > b // greater than
|
||
|
print a >= b // greater than or equal
|
||
|
print a < b // less than
|
||
|
print a <= b // less than or equal
|
||
|
|
||
|
// Indexing and slicing in a string is done with an index (or two)
|
||
|
// in square brackets. Use a 0-based index to count from the front,
|
||
|
// or a negative index to count from the end. Get a slice (substring)
|
||
|
// with two indices, separated by a colon. Either one may be omitted
|
||
|
// to extend the slice to the beginning or end of the string.
|
||
|
print "Indexing and slicing:"
|
||
|
print a[0] // get a character, starting with 0 ("H")
|
||
|
print a[1] // get second character ("e")
|
||
|
print a[-1] // negative numbers count from the end ("o")
|
||
|
print a[1:4] // get slice from 1 up to (but not including) 4 ("ell")
|
||
|
print a[1:-1] // same as above, but using a negative index
|
||
|
print a[1:] // get slice from 1 to the end ("ello")
|
||
|
print a[:2] // get slice from beginning up to 2 ("He")
|
||
|
|
||
|
// Note that strings in MiniScript are immutable. You can't reache
|
||
|
// into a string and change what characters it contains (but you can
|
||
|
// always create a new string with different characters).
|
||
|
```
|
||
|
|
||
|
### Lists
|
||
|
|
||
|
```
|
||
|
// A list is an ordered sequence of values of any type. You can
|
||
|
// iterate over a list with a for loop, or iterate over the indexes
|
||
|
// using .indexes.
|
||
|
x = ["alpha", "beta", "gamma", "delta"]
|
||
|
for item in x
|
||
|
print item
|
||
|
end for
|
||
|
for i in x.indexes
|
||
|
print "x[" + i + "] is " + x[i]
|
||
|
end for
|
||
|
|
||
|
// Indexing and slicing in a list is exactly like a string: use a
|
||
|
// 0-based index to count from the front, or a negative number to
|
||
|
// count from the end. Get a slice (subset) of a list with two
|
||
|
// indices, separated by a colon. Either one may be omitted
|
||
|
// to extend the slice to the beginning or end of the list.
|
||
|
print x[0] // alpha
|
||
|
print x[-1] // delta
|
||
|
print x[1:3] // [beta, gamma]
|
||
|
print x[2:] // [gamma, delta]
|
||
|
print x[:-1] // [alpha, beta, gamma]
|
||
|
|
||
|
// Lists support the following operators:
|
||
|
y = ["a", "be", "ce", "de"]
|
||
|
print "List ""math"":"
|
||
|
print x + y // list concatenation
|
||
|
print y * 3 // list replication
|
||
|
print x / 2 // list division
|
||
|
|
||
|
print "Comparisons:"
|
||
|
print x == y // equality test (note == rather than = here!)
|
||
|
print x != y // inequality
|
||
|
```
|
||
|
|
||
|
### Maps
|
||
|
|
||
|
```
|
||
|
// A map is a set of values associated with unique keys. Maps
|
||
|
// are an extremely powerful and versatile data type, used to
|
||
|
// represent data records, objects, sparse arrays, and much more.
|
||
|
// Create a map with curly braces; get or set a single value
|
||
|
// with square brackets. Keys and values may be any type.
|
||
|
// ("Key" and "index" mean the same thing in the context of a map.)
|
||
|
m = {1:"one", 2:"two"}
|
||
|
print m[1] // one
|
||
|
m[2] = "dos" // change the value associated with index 2
|
||
|
print m[2] // dos
|
||
|
|
||
|
// In the special case where the key (index) is a string that
|
||
|
// would be a valid variable name, there is an alternate to the
|
||
|
// square-bracket syntax called dot syntax. Just put the key,
|
||
|
// without quotes, after the map and a dot (period).
|
||
|
m.pi = 3.14 // equivalent to: m["pi"] = 3.14
|
||
|
print m["pi"] // 3.14
|
||
|
m["e"] = 2.72 // equivalent to: m.e = 2.72
|
||
|
print m.e // 2.72
|
||
|
|
||
|
// Maps support only the + operator, which combines all the key/value
|
||
|
// pairs from two maps into one.
|
||
|
m1 = {1:"one", 2:"two"}
|
||
|
m2 = {2:"dos", 3:"tres"}
|
||
|
print m1 + m2 // map concatenation
|
||
|
|
||
|
// You can iterate over the key/value pairs in a map with a for loop.
|
||
|
// On each iteration, the variable will be itself a little map with
|
||
|
// "key" and "value" indexes.
|
||
|
for kv in m1+m2
|
||
|
print kv.key + " -> " + kv.value
|
||
|
end for
|
||
|
|
||
|
// Note that the order of key/value pairs in a map is undefined.
|
||
|
// You should never rely on them appearing in a particular order
|
||
|
// when you print or iterate over a map.
|
||
|
```
|
||
|
|
||
|
### Functions
|
||
|
|
||
|
```
|
||
|
// Create a function in miniscript with a function...end function
|
||
|
// block. In most cases you will assign the result to a variable
|
||
|
// so you can call it later. If a function needs to return a
|
||
|
// a result, do that with the return keyword.
|
||
|
rollDie = function
|
||
|
return ceil(rnd * 6) // return a random number from 1-6
|
||
|
end function
|
||
|
print rollDie
|
||
|
print rollDie
|
||
|
|
||
|
// If it needs parameters, put them after function keyword inside
|
||
|
// parentheses. Parameters may have default values.
|
||
|
roll = function(numberOfDice, sides=6)
|
||
|
sum = 0
|
||
|
for i in range(1, numberOfDice)
|
||
|
sum = sum + ceil(rnd * sides)
|
||
|
end for
|
||
|
return sum
|
||
|
end function
|
||
|
print roll(2) // roll two 6-sided dice
|
||
|
print roll(2,20) // roll two 20-sided dice
|
||
|
|
||
|
// Variables are always local by default in MiniScript. The
|
||
|
// variables i and sum in the function above are not accessible
|
||
|
// outside the function, and disappear as soon as the function
|
||
|
// returns. (We'll talk more about variable scope later.)
|
||
|
|
||
|
// Parentheses are needed only if (1) you're passing arguments
|
||
|
// (parameter values) to the function, and (2) you're using the
|
||
|
// result as part of some larger statement. Notice how the first
|
||
|
// example above, rollDie did not need any parentheses because we
|
||
|
// weren't passing an arguments. Here's an example of a function
|
||
|
// that, like the built-in print function, is used as a statement
|
||
|
// by itself, and so does not need parentheses.
|
||
|
doRoll = function(numberOfDice, sides=6)
|
||
|
print "Rolling " + numberOfDice + "d" + sides + "..."
|
||
|
sum = 0
|
||
|
for i in range(1, numberOfDice)
|
||
|
roll = ceil(rnd * sides)
|
||
|
print "You rolled a " + roll + "."
|
||
|
sum = sum + roll
|
||
|
end for
|
||
|
print "Your total is: " + sum
|
||
|
end function
|
||
|
doRoll 3 // roll 3d6 -- note no parentheses needed
|
||
|
doRoll 3, 8 // same here, but rolling 3d6
|
||
|
|
||
|
// If you ever need to refer to a function without invoking it,
|
||
|
// you can do so with the @ operator.
|
||
|
f = @doRoll // makes f refer to the same function as doRoll
|
||
|
f 2,4 // rolls 2d4
|
||
|
```
|
||
|
|
||
|
### Classes and Objects
|
||
|
|
||
|
```
|
||
|
// MiniScript uses prototype-based inheritance. A class or object
|
||
|
// is just a map with a special __isa entry that points to the
|
||
|
// parent class. This is set automatically when you use the new
|
||
|
// operator.
|
||
|
Shape = {} // make a base class
|
||
|
Shape.sides = 0 // give it 0 sides by default
|
||
|
|
||
|
Square = new Shape // make a subclass of Shape called Square
|
||
|
Square.sides = 4 // override the number of sides
|
||
|
|
||
|
x = new Square // create an instance of the Square class
|
||
|
print x.sides // 4, because x is a Square and Square.sides is 4
|
||
|
|
||
|
// A method is just a function stored in a class (map). These
|
||
|
// are inherited through the __isa chain, just like other values.
|
||
|
// Within a method, the keyword self refers to the object on which
|
||
|
// the method was invoked (using dot syntax). This is how you
|
||
|
// refer to data or methods on the object.
|
||
|
Shape.describe = function
|
||
|
print
|
||
|
print "This is a " + self.sides + "-sided shape."
|
||
|
end function
|
||
|
x.describe // This is a 4-sided shape.
|
||
|
|
||
|
// Methods may be overridden (again just like values). In a
|
||
|
// subclass/instance method, you may use super to invoke the next
|
||
|
// version of the method up the inheritance chain, while still
|
||
|
// keeping self bound to the object this method was called on.
|
||
|
Square.describe = function
|
||
|
super.describe // first, do the standard description
|
||
|
print "It looks very squarish." // then add this
|
||
|
end function
|
||
|
x.describe
|
||
|
```
|
||
|
|
||
|
### More on Variable Scope
|
||
|
|
||
|
```
|
||
|
// Variables assignments in MiniScript always create or update
|
||
|
// a local variable, i.e., one that exists only within the function
|
||
|
// containing the assignment, unless dot syntax is used to specify
|
||
|
// some other scope.
|
||
|
x = 42 // here's a global variable called x
|
||
|
f = function
|
||
|
x = 1234 // make a local variable, also called x
|
||
|
print "Inside the function, x is now " + x
|
||
|
end function
|
||
|
f
|
||
|
print "Outside the function, x is " + x
|
||
|
|
||
|
// In the example above, the assignment to x inside the function
|
||
|
// has no effect on the global value of x, even though they happen
|
||
|
// to have the same name. (This is a Good Thing because it helps
|
||
|
// you avoid unintended side-effects in your code.) Global variables
|
||
|
// are generally discouraged, but if you must update one inside
|
||
|
// a function, you can use a "globals." prefix to do so.
|
||
|
f = function
|
||
|
print "Using the globals prefix..."
|
||
|
globals.x = 1234 // update the global variable x
|
||
|
print "Inside the function, x is now " + x
|
||
|
end function
|
||
|
f
|
||
|
print "Outside the function, x is " + x
|
||
|
|
||
|
// This is very similar to the "self." prefix used with
|
||
|
// class methods; in both cases, you are giving a more specific
|
||
|
// scope to a variable (which is really just specifying a map
|
||
|
// to index into with dot syntax).
|
||
|
|
||
|
// However there is an important difference: when READING (not
|
||
|
// assigning to) a variable, if the variable name is not found
|
||
|
// among the local variables, MiniScript will automatically look
|
||
|
// for a global variable of that name. Thus no "globals." prefix
|
||
|
// is needed when reading a variable, but only when assigning it.
|
||
|
count = 0
|
||
|
addToCount = function(amount=1)
|
||
|
globals.count = count + amount
|
||
|
end function
|
||
|
addToCount
|
||
|
addToCount
|
||
|
print "count is now: " + count
|
||
|
|
||
|
// In the addToCount function above, note how we need the globals
|
||
|
// prefix on the left-hand side of the assignment, since otherwise
|
||
|
// it would create a local variable. But we don't need it on the
|
||
|
// right-hand side, where we are merely reading the global value.
|
||
|
```
|
||
|
|
||
|
### Handy Intrinsic Methods
|
||
|
|
||
|
```
|
||
|
// Intrinsic methods are ones that are built into MiniScript or its
|
||
|
// environment. Particular MiniScript environments (e.g. Mini Micro,
|
||
|
// Soda, command-line MiniScript, some game using MiniScript as an
|
||
|
// embedded language, etc.) will probably add additional intrinsics.
|
||
|
// But there is a core of about 50 intrinsics that should always be
|
||
|
// available.
|
||
|
|
||
|
// Here's a quick demo of some of the most commonly used ones.
|
||
|
print abs(-42) // absolute value
|
||
|
print pi // get value of pi (yep, this is built in!)
|
||
|
print cos(pi) // cosine
|
||
|
print sqrt(100) // square root
|
||
|
print round(pi, 2) // round (to 2 decimal places)
|
||
|
print char(65) // get Unicode character 65
|
||
|
|
||
|
print
|
||
|
s = "Hello world!"
|
||
|
print s.upper // convert to upper case
|
||
|
print s.len // get length (number of characters)
|
||
|
print s.replace("Hello", "Heya") // string substitution
|
||
|
print s.split(" ") // split on spaces to make a list
|
||
|
print s.remove("l") // remove first occurrence of "l"
|
||
|
|
||
|
print
|
||
|
a = range(2,15,3) // make a list: 2 through 10, in steps of 3
|
||
|
print "a: " + a
|
||
|
print "a.len:" + a.len // get length (number of values)
|
||
|
print "a.sum:" + a.sum // get sum of adding all values together
|
||
|
print a.pop // pop off the last value
|
||
|
print a.pull // pull off the first value
|
||
|
print "popped and pulled: " + a
|
||
|
a.push 99 // push a new item onto the end
|
||
|
a.insert 0, 101 // insert a new item at index 0
|
||
|
print "after push and insert: " + a
|
||
|
a.remove 2 // remove index 2 from the list
|
||
|
print "after remove 2: " + a
|
||
|
s = a.join("#") // make a string by joining values with #
|
||
|
print s
|
||
|
|
||
|
print
|
||
|
m = {"one": "uno", "two": "dos", "three": "tres"}
|
||
|
print m.hasIndex("one") // check whether a key exists
|
||
|
print m.indexes // get all the indexes
|
||
|
print m.values // get all the values
|
||
|
m.remove "two" // remove an index (and its value)
|
||
|
print m
|
||
|
```
|
||
|
|
||
|
## Further Reading
|
||
|
|
||
|
* [MiniScript.org website](https://miniscript.org/) — center of the MiniScript universe
|
||
|
* [MiniScript Quick Reference](https://miniscript.org/files/MiniScript-QuickRef.pdf) — this tutorial, in one page
|
||
|
* [MiniScript User's Manual](https://miniscript.org/files/MiniScript-Manual.pdf) — more in-depth documentation
|
||
|
* [MiniScript Wiki](https://miniscript.org/wiki/) — community-driven documentation
|