shithub: scrax

ref: 86a1bffad7063d1f51dfe44c2126bbdf8b7fe5c8
dir: /core.js/

View raw version
import {MakeBlock, MakeSlot, complement} from "./model.js"

// slots
export let TextSlot = MakeSlot()
export let BooleanSlot = MakeSlot({numeric: true, normalise: () => "", canFit: block => block.type.output === "boolean"})
export let NumberSlot = MakeSlot({numeric: true, normalise: n => Number.isFinite(Number(n)) ? String(n) : ""})
export let IntegerSlot = MakeSlot({numeric: true, normalise: n => Number.isInteger(Number(n)) ? String(n) : ""})
export let PositiveSlot = MakeSlot({numeric: true, normalise: n => { let m = Number(n) ; return Number.isFinite(m) && m >= 0 ? String(n) : "" }})
export let NaturalSlot = MakeSlot({numeric: true, normalise: n => { let m = Number(n) ; return Number.isInteger(m) && m >= 0 ? String(n) : "" }})
export function EnumeratedSlot(options, type = {})
{
	return MakeSlot({canFit: () => false, ...type, normalise: n => String(options.includes(String(n)) ? n : options[0]), options})
}

// events
export let WhenFlagClicked = MakeBlock({hat: true, name: () => ["when", [getIcon(), "green flag", "flag", "gf", "@greenFlag"], "clicked"], category: "events"})

// control
export let Else = MakeBlock({stack: true, name: () => ["else"]})
export let If = MakeBlock({slots: [BooleanSlot], stack: true, name: condition => ["if", condition, "then"], category: "control", complement: Else.type})
export let Forever = MakeBlock({stack: true, name: () => ["forever"], category: "control", loop: true, cap: true})
export let WaitUntil = MakeBlock({slots: [BooleanSlot], name: condition => ["wait until", condition], category: "control"})
export let RepeatUntil = MakeBlock({slots: [BooleanSlot], stack: true, name: condition => ["repeat until", condition], category: "control", loop: true})
export let Repeat = MakeBlock({slots: [NaturalSlot], stack: true, name: seconds => ["repeat", seconds], category: "control", defaults: [10], loop: true})
export let Stop = MakeBlock({name: () => ["stop this script"], category: "control", cap: true})
export function IfElse(...args)
{
	let block = If(...args)
	complement(block, Else())
	return block
}

// operators
export let Add = MakeBlock({shape: "reporter", slots: [NumberSlot, NumberSlot], name: (a, b) => [a, "+", b], category: "operators", output: "number"})
export let Subtract = MakeBlock({shape: "reporter", slots: [NumberSlot, NumberSlot], name: (a, b) => [a, "-", b], category: "operators", output: "number"})
export let Multiply = MakeBlock({shape: "reporter", slots: [NumberSlot, NumberSlot], name: (a, b) => [a, "*", b], category: "operators", output: "number"})
export let Divide = MakeBlock({shape: "reporter", slots: [NumberSlot, NumberSlot], name: (a, b) => [a, "/", b], category: "operators", output: "number"})
export let Modulo = MakeBlock({shape: "reporter", slots: [NumberSlot, NumberSlot], name: (a, b) => [a, "mod", b], category: "operators", output: "number"})
export let LessThan = MakeBlock({shape: "reporter", slots: [TextSlot, TextSlot], name: (a, b) => [a, "<", b], category: "operators", defaults: ["", 50], output: "boolean"})
export let GreaterThan = MakeBlock({shape: "reporter", slots: [TextSlot, TextSlot], name: (a, b) => [a, ">", b], category: "operators", defaults: ["", 50], output: "boolean"})
export let Equals = MakeBlock({shape: "reporter", slots: [TextSlot, TextSlot], name: (a, b) => [a, "=", b], category: "operators", defaults: ["", 50], output: "boolean"})
export let And = MakeBlock({shape: "reporter", slots: [BooleanSlot, BooleanSlot], name: (a, b) => [a, "and", b], category: "operators", output: "boolean"})
export let Or = MakeBlock({shape: "reporter", slots: [BooleanSlot, BooleanSlot], name: (a, b) => [a, "or", b], category: "operators", output: "boolean"})
export let Not = MakeBlock({shape: "reporter", slots: [BooleanSlot], name: a => ["not", a], category: "operators", output: "boolean"})
export let Join = MakeBlock({shape: "reporter", slots: [TextSlot, TextSlot], name: (a, b) => ["join", a, b], category: "operators", defaults: ["apple ", "banana"], output: "text"})
export let TextIndex = MakeBlock({shape: "reporter", slots: [TextSlot, NaturalSlot], name: (a, b) => ["letter", b, "of", a], category: "operators", defaults: ["apple", 1], output: "text"})
export let TextLength = MakeBlock({shape: "reporter", slots: [TextSlot], name: a => ["length of", a], category: "operators", defaults: ["apple"], output: "natural"})
export let TextContains = MakeBlock({shape: "reporter", slots: [TextSlot, TextSlot], name: (a, b) => [a, "contains", b, "?"], category: "operators", defaults: ["apple", "a"], output: "boolean"})

// variables
export let Variable = MakeBlock({shape: "reporter", category: "variables", references: ["variable"]})
export let SetVariable = MakeBlock({slots: [TextSlot], name: (name, value) => ["set", name, "to", value], category: "variables", references: ["variable"], defaults: [0]})
export let ChangeVariable = MakeBlock({slots: [NumberSlot], name: (name, value) => ["change", name, "by", value], category: "variables", references: ["variable"], defaults: [1]})

// lists
export let List = MakeBlock({shape: "reporter", category: "lists", references: ["list"], output: "text"})
export let Append = MakeBlock({slots: [TextSlot], name: (name, value) => ["add", value, "to", name], category: "lists", references: ["list"], defaults: ["thing"]})
export let Delete = MakeBlock({slots: [NaturalSlot], name: (name, index) => ["delete", index, "of", name], category: "lists", references: ["list"], defaults: [1]})
export let Clear = MakeBlock({name: name => ["delete all of", name], category: "lists", references: ["list"]})
export let Insert = MakeBlock({slots: [NaturalSlot, TextSlot], name: (name, index, value) => ["insert", value, "at", index, "of", name], category: "lists", references: ["list"], defaults: [1, "thing"]})
export let Replace = MakeBlock({slots: [NaturalSlot, TextSlot], name: (name, index, value) => ["replace item", index, "of", name, "with", value], category: "lists", references: ["list"], defaults: [1, "thing"]})
export let Index = MakeBlock({shape: "reporter", slots: [NaturalSlot], name: (name, index) => ["item", index, "of", name], category: "lists", references: ["list"], defaults: [1]})
export let Find = MakeBlock({shape: "reporter", slots: [TextSlot], name: (name, value) => ["item # of", value, "in", name], category: "lists", references: ["list"], defaults: ["thing"], output: "natural"})
export let Length = MakeBlock({shape: "reporter", name: name => ["length of", name], category: "lists", references: ["list"], output: "natural"})
export let Contains = MakeBlock({shape: "reporter", slots: [TextSlot], name: (name, value) => [name, "contains", value, "?"], category: "lists", references: ["list"], defaults: ["thing"], output: "boolean"})

// my blocks
export let Define = MakeBlock({hat: true, category: "custom", references: ["custom"], complete: block => block.atomic = false, name: prototype => ["define", prototype]})
export let TextParameter = MakeBlock({shape: "reporter", category: "custom", references: ["text-parameter"]})
export let BooleanParameter = MakeBlock({shape: "reporter", category: "custom", references: ["boolean-parameter"], output: "boolean"})

function MakeCustom(name)
{
	return MakeBlock({
		category: "custom",
		name: (...inputs) =>
		{
			let name1 = []
			let i = 0
			for (let part of name) {
				if (typeof part === "string") name1.push(part)
				else name1.push(inputs[i++])
			}
			return name1
		},
		slots: name.filter(part => typeof part !== "string").map(part => part.type === "boolean" ? BooleanSlot : TextSlot),
		references: ["custom"],
		defaults: name.filter(part => typeof part !== "string").map(() => ""),
	})
}

export function Custom(name, ...inputs)
{
	return MakeCustom(name)(name, ...inputs)
}

let variableFns = [SetVariable, ChangeVariable]
let listFns = [Append, Delete, Clear, Insert, Replace, Index, Find, Length, Contains]

export let library = [
	WhenFlagClicked,
	Repeat, Forever, If, IfElse, WaitUntil, RepeatUntil, Stop,
	Add, Subtract, Multiply, Divide, Modulo, GreaterThan, LessThan, Equals, And, Or, Not, Join, TextIndex, TextLength, TextContains,
	...variableFns, ...listFns,
]

export function VariableLibrary(names)
{
	if (names.length === 0) return []
	let name = names[0]
	return [
		...names.map(name => () => Variable(name)),
		...variableFns.map(fn => () => fn(name)),
	]
}

export function ListLibrary(names)
{
	if (names.length === 0) return []
	let name = names[0]
	return [
		...names.map(name => () => List(name)),
		...listFns.map(fn => () => fn(name)),
	]
}

export function compareName(name0, name1)
{
	if (name0.length !== name1.length) return false
	for (let [i, part] of name0.entries()) {
		let part1 = name1[i]
		if ((typeof part === "string") !== (typeof part1 === "string")) return false
		if (typeof part === "string") {
			if (part !== part1) return false
		}
		else {
			if (part.type !== part1.type) return false
		}
	}
	return true
}

let icon = `<svg viewbox="0 0 24 24" fill="#6C6" stroke="#444"><path d="M20.45,5.37C20.71,4.71,20.23,4,19.52,4H13h-1H7V3c0-0.55-0.45-1-1-1h0C5.45,2,5,2.45,5,3v1v10v8h2v-8h4h1h7.52 c0.71,0,1.19-0.71,0.93-1.37L19,9L20.45,5.37z"/></svg>`

function getIcon()
{
	let span = document.createElement("span")
	span.insertAdjacentHTML("beforeend", icon)
	let svg = span.querySelector("svg")
	svg.style.setProperty("height", "1.5em")
	svg.style.setProperty("vertical-align", "middle")
	return span
}