diff --git a/Bin/PicoCalc SD/BOOT2040.uf2 b/Bin/PicoCalc SD/BOOT2040.uf2 new file mode 100644 index 0000000..3bf1ada Binary files /dev/null and b/Bin/PicoCalc SD/BOOT2040.uf2 differ diff --git a/Bin/PicoCalc SD/MACHIKAP.INI b/Bin/PicoCalc SD/MACHIKAP.INI new file mode 100644 index 0000000..27a9262 --- /dev/null +++ b/Bin/PicoCalc SD/MACHIKAP.INI @@ -0,0 +1,216 @@ +# MachiKania system +# initialization file + +# Specify the autoexec file + +AUTOEXEC=MACHIKAP.BAS + + +# Set the direction of LCD + +# HORIZONTAL # same as LCD0TURN +VERTICAL # same as LCD270TURN +# LCD180TURN +# LCD90TURN + + +# When using IPS-LCD, activate following line + +LCDINVERT + + +# Setup Initial Text width (42 or 80 for Type PU) + +# WIDTH42 +# WIDTH80 + + +# Decide if rotate button assignment when LCD vertical setting + +ROTATEBUTTONS +# NOROTATEBUTTONS + + +# Decide if output to USB serial port + +USBSERIALON +# USBSERIALOFF + + +# Decide if emulate buttons by keyboard by setting key codes +# Default: +# UP button: UP key +# DOWN button: DOWN key +# LEFT button: LEFT key +# RIGHT button: RIGHT key +# START button: Enter key +# FIRE button: Space key + +EMULATEBUTTONUP=38 +EMULATEBUTTONDOWN=40 +EMULATEBUTTONLEFT=37 +EMULATEBUTTONRIGHT=39 +EMULATEBUTTONSTART=13 +EMULATEBUTTONFIRE=32 + + +# Decide if output to LCD + +LCDOUTON +# LCDOUTOFF + + +# Decide if wait for 2.5 seconds in the beginning in debug mode + +DEBUGWAIT2500 +# NODEBUGWAIT2500 + + +# Infinite loop or reset at the end of program + +LOOPATEND +# RESETATEND + + +# Waiting time at the beginning in milli seconds (must be more than 499) + +STARTWAIT=500 + + +# What to do when an exception occurs + +# EXCRESET # Reset Machikania (default: off) +# EXCSCREENSHOT=EXCSCRS.BIN # Save screen shot as a file (default: off) +# EXCDUMP=EXDUMP.BIN # Dump memory to a file (default: off) + + +# Waiting time for USB keyboard connection in milli seconds (0: infinite) + +#WAIT4KEYBOARD=0 +WAIT4KEYBOARD=2000 + + +# Specify the keyboard type +# Enable the keyword below + +# 106KEY # Japanese Keyboard +101KEY # US Keyboard + + +# Setup Lock keys' status +# Comment out if not lock +# the key when initializing + +NUMLOCK # Num Lock Key +CAPSLOCK # Caps Lock Key +# SCRLLOCK # Scroll Lock Key + + +# Specify SPI pins +# Comment out for default settings +# Useful for small RP2040 boards like XIAO RP2040, RP2040-Zero, and Tiny-2040 +# Valid values for SPIMISO: 0, 4, 16, 20 (SPI0) +# SPIMISO: 8, 12, 24, 28 (SPI1) +# Valid values for SPIMOSI: 3, 7, 19, 23 (SPI0) +# SPIMOSI: 11, 15, 27 (SPI1) +# Valid values for SPICLK: 2, 6, 18, 22 (SPI0) +# SPICLK: 10, 14, 26 (SPI1) + +# SPIMISO=4 +# SPIMOSI=7 +# SPICLK=6 + + +# Specify UART pins +# Comment out for default settings +# Valid values for UARTTX: 0, 12, 16, 28 (UART0) +# UARTTX: 4, 8, 20, 24 (UART1) +# Valid values for UARTRX: 1, 13, 17, 29 (UART0) +# UARTRX: 5, 9, 21, 25 (UART1) + +# UARTTX=4 +# UARTRX=5 + + +# Specify I2C pins +# Comment out for default settings +# Note that I2C1 cannot be used for PicoCalc as it is used for keyboard +# Valid values for I2CSDA: 0, 4, 8, 12, 16, 20, 24, 28 (I2C0) +# I2CSDA: 2, 6, 10, 14, 18, 22, 26 (I2C1) +# Valid values for I2CSCL: 1, 5, 9, 13, 17, 21, 25, 29 (I2C0) +# I2CSCL: 3, 7, 11, 15, 19, 23, 27 (I2C1) + +# I2CSDA=0 +# I2CSCL=1 + + +# Use Real Time Clock (RTC) for saving files by file system +# If RTC is not use, timestamp of save file will be 2020/01/01 +# Remove comment to enable RTC for file system in following line + +# RTCFILE + +# Timezone used for RTC setting by NTP + +# TIMEZONE=9 # JST +# TIMEZONE=8.5 # ACST +# TIMEZONE=5.75 # Nepal Standard Time +TIMEZONE=0 # UTC +# TIMEZONE=-8 # PST +# TIMEZONE=-7 # PDT +# TIMEZONE=-5 # EST +# TIMEZONE=-4 # EDT + + +# Connect to wifi when starting (Pico W only) + +# USEWIFI + + +# Set country code for wifi connection (Pico W only) + +WIFICOUNTRY=JP +# WIFICOUNTRY=US + + +# Define wifi SSID and password (Pico W only) + +# WIFISSID=xxxx +# WIFIPASSWD=xxxx + + +# Define hostname (Pico W only; default: PicoW) + +# HOSTNAME=machikania + + +# Define NTP server (Pico W only) + +NTPSERVER=pool.ntp.org + + +# Enable following line to connect to NTP server in the beginning after power on (Pico W only) +# Make sure that USEWIFI is enabled when using this feature + +# INITIALNTP + +# Enable following line to show timestamp when selecting files +# SHOWTIMESTAMP + +# Sort order when selecting files +# 0:A...Z 1:Z...A 2:OLD...NEW 3:NEW...OLD +FILESORTBY=0 + + +# Set help file used in editor +# Use help-e.txt for English help +# Use help-k.txt for Japanese Kana help + +HELPFILE=/docs/help-e.txt +#HELPFILE=/docs/help-k.txt + + +# Assign the additional PWM (4-9) to GPIO number +# Note that this is not MachiKania port number, but RP2040/2350 GPIO number (0-29) + +# PWM4=21 diff --git a/Bin/PicoCalc SD/README.md b/Bin/PicoCalc SD/README.md index 1d83f10..965f5ef 100644 --- a/Bin/PicoCalc SD/README.md +++ b/Bin/PicoCalc SD/README.md @@ -1,75 +1,209 @@ -# PicoCalc SD +# PicoCalc SD v0.6 -This repository contains the official factory image and files for the **PicoCalc SD card**. It includes essential firmware, applications, and system files required for the proper functioning of **PicoCalc**. - -## Directory Overview - -``` +PicoCalc SD/ +├── BellLabs_Fine.mp3 ├── bifdiag.bas +├── BOOT2040.uf2 +├── bootloader_pico.uf2 +├── cc +│   ├── edit.lua +│   ├── expect.lua +│   ├── internal +│   │   ├── menu.lua +│   │   └── syntax +│   │   ├── errors.lua +│   │   ├── init.lua +│   │   ├── lexer.lua +│   │   └── parser.lua +│   └── pretty.lua ├── Chessnovice_johnybot.nes -├── firmware -│   ├── PicoCalc_Fuzix_v1.0.img +├── fonts +│   ├── 6x10.fnt +│   ├── Acer8x8.fnt +│   ├── Haxor12.fnt +│   ├── HP6x8.fnt +│   ├── HP8x8.fnt +│   └── ProggyClean.fnt +├── lorenz.bas +├── lua +│   ├── asteroids.lua +│   ├── boxworld.bmp +│   ├── boxworld.lua +│   ├── browser.lua +│   ├── bubble.lua +│   ├── mandelbrot.lua +│   └── piano.lua +├── MACHIKAP.INI +├── main.lua +├── mand.bas +├── pico1-apps +│   ├── MicroPython_fa8b24c.uf2 +│   ├── phyllosoma_kb.uf2 │   ├── PicoCalc_Fuzix_v1.0.uf2 │   ├── PicoCalc_MP3Player_v0.5.uf2 │   ├── PicoCalc_NES_v1.0.uf2 -│   ├── PicoCalc_PicoMite_v1.0.uf2 -│   ├── PicoCalc_stm32 #PicoCalc keyboard firmware -│   │   ├── PicoCalc_firmware_v1.0.bin -│   │   └── PicoCalc_firmware_v1.0.hex -│   └── PicoCalc_uLisp_v1.0.uf2 -├── lorenz.bas -├── mand.bas +│   ├── PicoCalc_uLisp_v1.1.uf2 +│   ├── picolua_daf20a2.uf2 +│   ├── PicoMite_v6.02.01b4_beta.uf2 +│   └── Picoware_v1.6.9.uf2 ├── picocalc.bmp +├── picoware +│   ├── apps +│   │   ├── Calculator.mpy +│   │   ├── cat-fact.mpy +│   │   ├── counter.mpy +│   │   ├── flip_social +│   │   │   ├── __init__.py +│   │   │   ├── password.mpy +│   │   │   ├── run.mpy +│   │   │   ├── settings.mpy +│   │   │   └── username.mpy +│   │   ├── FlipSocial.mpy +│   │   ├── games +│   │   │   ├── 2048.mpy +│   │   │   ├── Breakout.mpy +│   │   │   ├── example.mpy +│   │   │   ├── Flappy Bird.mpy +│   │   │   ├── flip_world +│   │   │   │   ├── assets.mpy +│   │   │   │   ├── general.mpy +│   │   │   │   ├── __init__.py +│   │   │   │   ├── player.mpy +│   │   │   │   ├── run.mpy +│   │   │   │   └── sprite.mpy +│   │   │   ├── FlipWorld.mpy +│   │   │   ├── free_roam +│   │   │   │   ├── dynamic_map.mpy +│   │   │   │   ├── game.mpy +│   │   │   │   ├── __init__.py +│   │   │   │   ├── maps.mpy +│   │   │   │   ├── player.mpy +│   │   │   │   └── sprite.mpy +│   │   │   ├── Free Roam.mpy +│   │   │   ├── game_of_life.mpy +│   │   │   ├── Maze Runner.mpy +│   │   │   ├── Minesweeper.mpy +│   │   │   ├── Pong.mpy +│   │   │   ├── Snake.mpy +│   │   │   ├── Soduko.mpy +│   │   │   ├── Space Invaders.mpy +│   │   │   ├── Tetris.mpy +│   │   │   └── Tower Defense.mpy +│   │   ├── Graph.mpy +│   │   ├── hello_color.mpy +│   │   ├── keyboard-simple.mpy +│   │   ├── loading-simple.mpy +│   │   ├── menu-simple.mpy +│   │   ├── random-object.mpy +│   │   ├── screensavers +│   │   │   ├── Bubble Universe.mpy +│   │   │   ├── Clock.mpy +│   │   │   ├── Cube.mpy +│   │   │   ├── DVI Bounce.mpy +│   │   │   ├── Fire Effect.mpy +│   │   │   ├── Matrix Rain.mpy +│   │   │   ├── Patterns.mpy +│   │   │   ├── PicoFlower.mpy +│   │   │   ├── Plasma Wave.mpy +│   │   │   └── Yin-Yang.mpy +│   │   ├── Serial Terminal.mpy +│   │   ├── storage-simple.mpy +│   │   ├── textbox-simple.mpy +│   │   ├── Text Editor.mpy +│   │   └── Weather.mpy +│   └── settings └── README.md + +Since PicoCalc SD v0.6 we used [uf2loader](https://github.com/pelrun/uf2loader.git) as main loader. + +All uf2 files in folder **pico1-apps** will be showed up in menu. + +Once a uf2 got flashed and ran, next time you can use menu item **[Default App]** to directly run it without flashing it again. + +Important files: + +* bootloader_pico.uf2 + uf2loader main program, should be flashed into pico. +* BOOT2040.uf2 + uf2loader Menu UI, it is a very important file, do not delete or edit it unless you know what you are doing. + + +## Path: pico1-apps/PicoCalc_Fuzix_v1.0.uf2 (Download) + +[Fuzix](https://github.com/EtchedPixels/FUZIX.git) is an open-source, lightweight Unix-like operating system specifically designed for 8-bit and other resource-constrained processors. + +Patches for PicoCalc are [here](https://github.com/clockworkpi/PicoCalc/tree/master/Code/FUZIX) + +## Path: pico1-apps/PicoCalc_NES_v1.0.uf2 (Download) + +A simple NES emulator for PicoCalc, it will scan all nes files in the root of SD card. +Given the resource constraints of the Pico, it is recommended to only run NES games that are less than **44KB** in size. + +## Path: pico1-apps/picolua_daf20a2.uf2 (Download) + +https://github.com/Lana-chan/picocalc_lua +A Lua interpreter for PicoCalc. It contains a REPL, basic API to draw graphics, read keys and access the SD filesystem. + +## Path: pico1-apps/Picoware_v1.6.9.uf2 (Download) + +https://github.com/jblanked/Picoware + +An Open-source custom firmware for the PicoCalc, Video Game Module, and other Raspberry Pi Pico devices. + +Here is the version based on MicroPython. + +## Path: pico1-apps/phyllosoma_kb.uf2 (Download) + +[MachiKania Phyllosoma](https://github.com/machikania/phyllosoma/releases) is a BASIC compiler for ARMv6-M with excellent performance., especially for Raspberry Pi Pico. + + +## Path: pico1-apps/PicoCalc_MP3Player_v0.5.uf2 (Download) + +[PicoCalc_MP3Player](https://github.com/clockworkpi/PicoCalc/tree/master/Code/MP3Player) is a simple MP3 playback program based on the [YAHAL](https://git.fh-aachen.de/Terstegge/YAHAL + +) framework. + + +## Path: pico1-apps/PicoCalc_uLisp_v1.1.uf2 (Download) + +http://www.ulisp.com/show?56ZO + +A self-contained Lisp computer for PicoCalc. + +## Path: pico1-apps/PicoMite_v6.02.01b4_beta.uf2 (Download) + +[The PicoMite](https://geoffg.net/picomite.html) is a complete operating system with a Microsoft BASIC compatible interpreter and extensive hardware support including touch sensitive LCD panels, SD Cards, WiFi/Internet and much more. + + +``` +Copyright and Acknowledgments + +The PicoMite firmware and MMBasic is copyright 2011-2025 by Geoff Graham and Peter Mather 2016-2025. +1-Wire Support is copyright 1999-2006 Dallas Semiconductor Corporation and 2012 Gerard Sexton. +FatFs (SD Card) driver is copyright 2014, ChaN. +WAV, MP3, and FLAC file support is copyright 2019 David Reid. +JPG support is thanks to Rich Geldreich +The pico-sdk is copyright 2021 Raspberry Pi (Trading) Ltd. +TinyUSB is copyright tinyusb.org +LittleFS is copyright Christopher Haster +Thomas Williams and Gerry Allardice for MMBasic enhancements +The VGA driver code was derived from work by Miroslav Nemecek +The CRC calculations are copyright Rob Tillaart +The compiled object code (the .uf2 file) for the PicoMite firmware is free software: you can use or redistribute +it as you please. The source code is on GitHub ( https://github.com/UKTailwind/PicoMiteAllVersions ) and +can be freely used subject to some conditions (see the header in the source files). +This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY, without even +the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ``` -## Flashing the Factory SD Image -To restore your **PicoCalc SD card** to its factory state, follow these steps: +## Path: pico1-apps/MicroPython_fa8b24c.uf2 (Download) -### Requirements: -- A microSD card (at least **16GB** recommended) -- A computer with **Linux/macOS/Windows** -- A microSD card reader +https://github.com/zenodante/PicoCalc-micropython-driver -## Custom Partitioning +[MicroPython](https://micropython.org/) is a lean and efficient implementation of the Python 3 programming language that includes a small subset of the Python standard library and is optimised to run on microcontrollers and in constrained environments. -The **PicoCalc SD card** uses a dual-partition structure: +Here is the MicroPython Drivers compatible with PicoCalc. -| Partition | Size | Format | Purpose | -|-----------|--------|--------|---------| -| `/dev/sdX1` | Remaining space | **FAT32** | Storage for PicoMite, uLisp, NES Emulator, etc. | -| `/dev/sdX2` | **32MB** | **fuzix filesystem** | Root filesystem for **FUZIX** | -### Manually Partitioning an SD Card - -If you need to manually create an SD card for **PicoCalc**, use the provided **partitioning script**: - -```bash -wget https://github.com/clockworkpi/PicoCalc/raw/refs/heads/master/Code/scripts/partition_usb_32mb.sh -chmod +x partition_usb_32mb.sh -sudo ./partition_usb_32mb.sh sdb -``` -*(Replace `sdb` with your actual SD card device.)* - -### Flashing the FUZIX 32MB Image -- Download the FUZIX image: - [PicoCalc_Fuzix_v1.0.img](https://github.com/clockworkpi/PicoCalc/blob/master/Bin/PicoCalc%20SD/firmware/PicoCalc_Fuzix_v1.0.img) - -- Flash the image to the second partition: -```bash -sudo dd if=filesystem.img of=/dev/sdb2 -``` - -Please check the wiki for details - -https://github.com/clockworkpi/PicoCalc/wiki/How-to-Create-an-Official-PicoCalc-SD-Card - -## Notes -- The **USB Type-C port** is the default **serial port** for **PicoCalc**, not the Micro USB port. -- FUZIX supports a maximum **32MB** root filesystem. -- Ensure you backup your data before modifying the SD card. - ---- -For more details, visit the official **[PicoCalc GitHub Repository](https://github.com/clockworkpi/PicoCalc)**. diff --git a/Bin/PicoCalc SD/bootloader_pico.uf2 b/Bin/PicoCalc SD/bootloader_pico.uf2 new file mode 100644 index 0000000..2aa7d0b Binary files /dev/null and b/Bin/PicoCalc SD/bootloader_pico.uf2 differ diff --git a/Bin/PicoCalc SD/cc/edit.lua b/Bin/PicoCalc SD/cc/edit.lua new file mode 100644 index 0000000..05b1d99 --- /dev/null +++ b/Bin/PicoCalc SD/cc/edit.lua @@ -0,0 +1,815 @@ +-- SPDX-FileCopyrightText: 2017 Daniel Ratcliffe +-- +-- SPDX-License-Identifier: LicenseRef-CCPL + +-- Get file to edit +local tArgs = { ... } +if #tArgs == 0 then + local programName = "edit" + print("Usage: " .. programName .. " ") + return +end + +-- Error checking +local sPath = tArgs[1] +local bReadOnly = false +if fs.exists(sPath) and fs.isDir(sPath) then + print("Cannot edit a directory.") + return +end + +local x, y = 1, 1 +local w, h = term.getSize() +local scrollX, scrollY = 0, 0 + +local tLines, tLineLexStates = {}, {} +local bRunning = true + +-- colors +local isColor = true +local highlightColor, keywordColor, textColor, bgColor, errorColor +if isColor then + bgColor = colors.black + textColor = colors.white + highlightColor = colors.yellow + keywordColor = colors.yellow + errorColor = colors.red +else + bgColor = colors.black + textColor = colors.white + highlightColor = colors.white + keywordColor = colors.white + errorColor = colors.white +end + +-- Menus +local menu = require "cc.internal.menu" +local current_menu +local menu_items = {} +if not bReadOnly then + table.insert(menu_items, "Save") +end +--[[if shell.openTab then + table.insert(menu_items, "Run") +end +if peripheral.find("printer") then + table.insert(menu_items, "Print") +end]] +table.insert(menu_items, "Run") +table.insert(menu_items, "Exit") + +local status_ok, status_text +local function set_status(text, ok) + status_ok = ok ~= false + status_text = text +end + +if bReadOnly then + set_status("File is read only", false) +elseif fs.getFreeSpace(sPath) < 1024 then + set_status("Disk is low on space", false) +else + local message + message = "Press Ctrl to access menu" + + if #message > w - 5 then + message = "Press Ctrl for menu" + end + + set_status(message) +end + +local function load(_sPath) + tLines = {} + if fs.exists(_sPath) then + bReadOnly = fs.isReadOnly(_sPath) + local file = fs.open(_sPath, "r") + local sLine = file:readLine() + while sLine do + table.insert(tLines, sLine) + table.insert(tLineLexStates, false) + sLine = file:readLine() + end + file:close() + end + + if #tLines == 0 then + table.insert(tLines, "") + table.insert(tLineLexStates, false) + end +end + +local function save(_sPath, fWrite) + -- Create intervening folder + --[[local sDir = _sPath:sub(1, _sPath:len() - fs.getName(_sPath):len()) + if not fs.exists(sDir) then + fs.makeDir(sDir) + end]] + + -- Save + local file, fileerr + local function innerSave() + file, fileerr = fs.open(_sPath, "w") + if file then + if file then + fWrite(file) + end + else + error("Failed to open " .. _sPath) + end + end + + local ok, err = pcall(innerSave) + if file then + file:close() + end + return ok, err, fileerr +end + + +local tokens = require "cc.internal.syntax.parser".tokens +local lex_one = require "cc.internal.syntax.lexer".lex_one + +local token_colors = { + [tokens.STRING] = colors.red, + [tokens.COMMENT] = colors.green, + [tokens.NUMBER] = colors.magenta, + -- Keywords + [tokens.AND] = keywordColor, + [tokens.BREAK] = keywordColor, + [tokens.DO] = keywordColor, + [tokens.ELSE] = keywordColor, + [tokens.ELSEIF] = keywordColor, + [tokens.END] = keywordColor, + [tokens.FALSE] = keywordColor, + [tokens.FOR] = keywordColor, + [tokens.FUNCTION] = keywordColor, + [tokens.GOTO] = keywordColor, + [tokens.IF] = keywordColor, + [tokens.IN] = keywordColor, + [tokens.LOCAL] = keywordColor, + [tokens.NIL] = keywordColor, + [tokens.NOT] = keywordColor, + [tokens.OR] = keywordColor, + [tokens.REPEAT] = keywordColor, + [tokens.RETURN] = keywordColor, + [tokens.THEN] = keywordColor, + [tokens.TRUE] = keywordColor, + [tokens.UNTIL] = keywordColor, + [tokens.WHILE] = keywordColor, +} +-- Fill in the remaining tokens. +for _, token in pairs(tokens) do + if not token_colors[token] then token_colors[token] = textColor end +end + +local lex_context = { line = function() end, report = function() end } + +local tCompletions +local nCompletion + +local tCompleteEnv = _ENV +local function complete(sLine) + --[[if settings.get("edit.autocomplete") then + local nStartPos = string.find(sLine, "[a-zA-Z0-9_%.:]+$") + if nStartPos then + sLine = string.sub(sLine, nStartPos) + end + if #sLine > 0 then + return textutils.complete(sLine, tCompleteEnv) + end + end + return nil]] +end + +local function recomplete() + local sLine = tLines[y] + if not bReadOnly and x == #sLine + 1 then + tCompletions = complete(sLine) + if tCompletions and #tCompletions > 0 then + nCompletion = 1 + else + nCompletion = nil + end + else + tCompletions = nil + nCompletion = nil + end +end + +local function writeCompletion(sLine) + if nCompletion then + local sCompletion = tCompletions[nCompletion] + term.setTextColor(colors.white) + term.setBackgroundColor(colors.grey) + term.write(sCompletion) + term.setTextColor(textColor) + term.setBackgroundColor(bgColor) + end +end + +--- Check if two values are equal. If both values are lists, then the contents will be +-- checked for equality, to a depth of 1. +-- +-- @param x The first value. +-- @param x The second value. +-- @treturn boolean Whether the values are equal. +local function shallowEqual(x, y) + if x == y then return true end + + if type(x) ~= "table" or type(y) ~= "table" then return false end + if #x ~= #y then return false end + + for i = 1, #x do if x[i] ~= y[i] then return false end end + return true +end + +local function redrawLines(line, endLine) + term.setCursorBlink(false) + if not endLine then endLine = line end + + local color = term.getTextColor() + + -- Highlight all lines between line and endLine, highlighting further lines if their + -- lexer state has changed and aborting at the end of the screen. + local changed = false + while (changed or line <= endLine) and line - scrollY < h do + term.setCursorPos(1 - scrollX, line - scrollY) + term.clearLine() + + local contents = tLines[line] + if not contents then break end + + -- Lex our first token, either taking our continuation state (if present) or + -- the default lexer. + local pos, token, _, finish, continuation = 1 + local lex_state = tLineLexStates[line] + if lex_state then + token, finish, _, continuation = lex_state[1](lex_context, contents, table.unpack(lex_state, 2)) + else + token, _, finish, _, continuation = lex_one(lex_context, contents, 1) + end + + while token do + -- start at scrollX + if finish >= scrollX+1 then + -- Print out that token + local new_color = token_colors[token] + if new_color ~= color then + term.setTextColor(new_color) + color = new_color + end + -- limit printed line to screen + if pos < scrollX+1 then pos = scrollX+1 end + local cap = finish < scrollX+w and finish or scrollX+w + term.write(contents:sub(pos, cap)) + end + + pos = finish + 1 + + -- end if we're past the screen width + if pos > scrollX+w then break end + + -- If we have a continuation, then we've reached the end of the line. Abort. + if continuation then break end + + -- Otherwise lex another token and continue. + token, _, finish, _, continuation = lex_one(lex_context, contents, pos) + end + + -- Print the rest of the line. We don't strictly speaking need this, as it will + -- only ever contain whitespace. + term.write(contents:sub(pos)) + + if line == y and x == #contents + 1 then + writeCompletion() + color = term.getTextColor() + end + + line = line + 1 + + -- Update the lext state of the next line. If that has changed, then + -- re-highlight it too. We store the continuation as nil rather than + -- false, to ensure we use the array part of the table. + if continuation == nil then continuation = false end + if tLineLexStates[line] ~= nil and not shallowEqual(tLineLexStates[line], continuation) then + tLineLexStates[line] = continuation or false + changed = true + else + changed = false + end + end + + term.setTextColor(colors.white) + term.setCursorPos(x - scrollX, y - scrollY) +end + +local function redrawText() + redrawLines(scrollY + 1, scrollY + h - 1) +end + +local function redrawMenu() + term.setCursorBlink(false) + + -- Clear line + term.setCursorPos(1, h) + term.clearLine() + + term.setCursorPos(1, h) + if current_menu then + -- Draw menu + menu.draw(current_menu) + else + -- Draw status + term.setTextColor(status_ok and highlightColor or errorColor) + term.write(status_text) + term.setTextColor(textColor) + + -- Draw line numbers + term.setCursorPos(w - #("Ln " .. y) + 1, h) + term.setTextColor(highlightColor) + term.write("Ln ") + term.setTextColor(textColor) + term.write(y) + end + + -- Reset cursor + term.setCursorPos(x - scrollX, y - scrollY) + term.setCursorBlink(not current_menu) +end + +local tMenuFuncs = { + Save = function() + if bReadOnly then + set_status("Access denied", false) + else + local ok, _, fileerr = save(sPath, function(file) + for _, sLine in ipairs(tLines) do + file:writeLine(sLine) + end + end) + if ok then + set_status("Saved to " .. sPath) + else + if fileerr then + set_status("Error saving: " .. fileerr, false) + else + set_status("Error saving to " .. sPath, false) + end + end + end + redrawMenu() + end, + --[[Print = function() + local printer = peripheral.find("printer") + if not printer then + set_status("No printer attached", false) + return + end + + local nPage = 0 + local sName = fs.getName(sPath) + if printer.getInkLevel() < 1 then + set_status("Printer out of ink", false) + return + elseif printer.getPaperLevel() < 1 then + set_status("Printer out of paper", false) + return + end + + local screenTerminal = term.current() + local printerTerminal = { + getCursorPos = printer.getCursorPos, + setCursorPos = printer.setCursorPos, + getSize = printer.getPageSize, + write = printer.write, + } + printerTerminal.scroll = function() + if nPage == 1 then + printer.setPageTitle(sName .. " (page " .. nPage .. ")") + end + + while not printer.newPage() do + if printer.getInkLevel() < 1 then + set_status("Printer out of ink, please refill", false) + elseif printer.getPaperLevel() < 1 then + set_status("Printer out of paper, please refill", false) + else + set_status("Printer output tray full, please empty", false) + end + + term.redirect(screenTerminal) + redrawMenu() + term.redirect(printerTerminal) + + sleep(0.5) + end + + nPage = nPage + 1 + if nPage == 1 then + printer.setPageTitle(sName) + else + printer.setPageTitle(sName .. " (page " .. nPage .. ")") + end + end + + local old_menu = current_menu + current_menu = nil + term.redirect(printerTerminal) + local ok, error = pcall(function() + term.scroll() + for _, sLine in ipairs(tLines) do + print(sLine) + end + end) + term.redirect(screenTerminal) + if not ok then + print(error) + end + + while not printer.endPage() do + set_status("Printer output tray full, please empty") + redrawMenu() + sleep(0.5) + end + current_menu = old_menu + + if nPage > 1 then + set_status("Printed " .. nPage .. " Pages") + else + set_status("Printed 1 Page") + end + redrawMenu() + end,]] + Exit = function() + bRunning = false + end, + Run = function() + local sTempPath = "~temp.lua" + --[[if fs.exists(sTempPath) then + set_status("Error saving to " .. sTempPath, false) + return + end]] + local ok = save(sTempPath, function(file) + for _, sLine in ipairs(tLines) do + file:writeLine(sLine) + end + end) + if ok then + collectgarbage() + local f, err = loadfile(sTempPath) + if not f then + set_status(tostring(err)) + else + term.clear() + keys.flush() + print("Free memory: "..sys.freeMemory()) + set_status("Press Ctrl to access menu") + local status, err = pcall(f) + draw.enableBuffer(false) + if not status then print(tostring(err)) end + term.setCursorPos(1, h) + term.write("Press any key...") + keys.flush() + keys.wait(false, true) + end + --fs.delete(sTempPath) + else + set_status("Error saving to " .. sTempPath, false) + end + collectgarbage() + term.clear() + redrawMenu() + redrawText() + end, +} + +local function setCursor(newX, newY) + local _, oldY = x, y + x, y = newX, newY + local screenX = x - scrollX + local screenY = y - scrollY + + local bRedraw = false + if screenX < 1 then + scrollX = x - 1 + screenX = 1 + bRedraw = true + elseif screenX > w then + scrollX = x - w + screenX = w + bRedraw = true + end + + if screenY < 1 then + scrollY = y - 1 + screenY = 1 + bRedraw = true + elseif screenY > h - 1 then + scrollY = y - (h - 1) + screenY = h - 1 + bRedraw = true + end + + recomplete() + if bRedraw then + redrawText() + elseif y ~= oldY then + redrawLines(math.min(y, oldY), math.max(y, oldY)) + else + redrawLines(y) + end + term.setCursorBlink(not current_menu); + + redrawMenu() +end + +-- Actual program functionality begins +load(sPath) + +term.setBackgroundColor(bgColor) +term.clear() +term.setCursorPos(x, y) + +recomplete() +redrawText() +redrawMenu() + +local function acceptCompletion() + if nCompletion then + -- Append the completion + local sCompletion = tCompletions[nCompletion] + tLines[y] = tLines[y] .. sCompletion + setCursor(x + #sCompletion, y) + end +end + +local function handleMenuEvent(key) + assert(current_menu) + + local result = menu.handle_event(current_menu, key) + if result == false then + current_menu = nil + redrawMenu() + elseif result ~= nil then + tMenuFuncs[result]() + current_menu = nil + redrawMenu() + end +end + +-- Handle input +while bRunning do + term.setCursorBlink(not current_menu) + state, modifiers, key = keys.wait() + + if state == keys.states.pressed or state == keys.states.longHold then + if current_menu then + handleMenuEvent(key) + else + if key == keys.up then + if nCompletion then + -- Cycle completions + nCompletion = nCompletion - 1 + if nCompletion < 1 then + nCompletion = #tCompletions + end + redrawLines(y) + + elseif y > 1 then + -- Move cursor up + setCursor( + math.min(x, #tLines[y - 1] + 1), + y - 1 + ) + end + + elseif key == keys.down then + if nCompletion then + -- Cycle completions + nCompletion = nCompletion + 1 + if nCompletion > #tCompletions then + nCompletion = 1 + end + redrawLines(y) + + elseif y < #tLines then + -- Move cursor down + setCursor( + math.min(x, #tLines[y + 1] + 1), + y + 1 + ) + end + + elseif key == keys.tab and not bReadOnly then + if nCompletion and x == #tLines[y] + 1 then + -- Accept autocomplete + acceptCompletion() + else + -- Indent line + local sLine = tLines[y] + tLines[y] = string.sub(sLine, 1, x - 1) .. "\t" .. string.sub(sLine, x) + setCursor(x + 1, y) + end + + elseif key == keys.pageUp then + -- Move up a page + local newY + if y - (h - 1) >= 1 then + newY = y - (h - 1) + else + newY = 1 + end + setCursor( + math.min(x, #tLines[newY] + 1), + newY + ) + elseif key == keys.pageDown then + -- Move down a page + local newY + if y + (h - 1) <= #tLines then + newY = y + (h - 1) + else + newY = #tLines + end + local newX = math.min(x, #tLines[newY] + 1) + setCursor(newX, newY) + + elseif key == keys.home then + -- Move cursor to the beginning + if x > 1 then + setCursor(1, y) + end + + elseif key == keys["end"] then + -- Move cursor to the end + local nLimit = #tLines[y] + 1 + if x < nLimit then + setCursor(nLimit, y) + end + + elseif key == keys.left then + if x > 1 then + -- Move cursor left + setCursor(x - 1, y) + elseif x == 1 and y > 1 then + setCursor(#tLines[y - 1] + 1, y - 1) + end + + elseif key == keys.right then + local nLimit = #tLines[y] + 1 + if x < nLimit then + -- Move cursor right + setCursor(x + 1, y) + elseif nCompletion and x == #tLines[y] + 1 then + -- Accept autocomplete + acceptCompletion() + elseif x == nLimit and y < #tLines then + -- Go to next line + setCursor(1, y + 1) + end + + elseif key == keys.delete and not bReadOnly then + local nLimit = #tLines[y] + 1 + if x < nLimit then + local sLine = tLines[y] + tLines[y] = string.sub(sLine, 1, x - 1) .. string.sub(sLine, x + 1) + recomplete() + redrawLines(y) + elseif y < #tLines then + tLines[y] = tLines[y] .. tLines[y + 1] + table.remove(tLines, y + 1) + table.remove(tLineLexStates, y + 1) + recomplete() + redrawText() + end + + elseif key == keys.backspace and not bReadOnly then + if x > 1 then + -- Remove character + local sLine = tLines[y] + if x > 4 and string.sub(sLine, x - 4, x - 1) == " " and not string.sub(sLine, 1, x - 1):find("%S") then + tLines[y] = string.sub(sLine, 1, x - 5) .. string.sub(sLine, x) + setCursor(x - 4, y) + else + tLines[y] = string.sub(sLine, 1, x - 2) .. string.sub(sLine, x) + setCursor(x - 1, y) + end + elseif y > 1 then + -- Remove newline + local sPrevLen = #tLines[y - 1] + tLines[y - 1] = tLines[y - 1] .. tLines[y] + table.remove(tLines, y) + table.remove(tLineLexStates, y) + setCursor(sPrevLen + 1, y - 1) + redrawText() + end + + elseif (key == keys.enter) and not bReadOnly then + -- Newline + local sLine = tLines[y] + local _, spaces = string.find(sLine, "^[ ]+") + if not spaces then + spaces = 0 + end + tLines[y] = string.sub(sLine, 1, x - 1) + table.insert(tLines, y + 1, string.rep(' ', spaces) .. string.sub(sLine, x)) + table.insert(tLineLexStates, y + 1, false) + setCursor(spaces + 1, y + 1) + redrawText() + + elseif key == keys.control then + current_menu = menu.create(menu_items) + redrawMenu() + + else + if keys.isPrintable(key) and not bReadOnly then + -- Input text + local sLine = tLines[y] + tLines[y] = string.sub(sLine, 1, x - 1) .. key .. string.sub(sLine, x) + setCursor(x + 1, y) + end + end + end + end + + --[[elseif event[1] == "paste" and not bReadOnly then + -- Close menu if open + if current_menu then + current_menu = nil + redrawMenu() + end + + -- Input text + local text = event[2] + local sLine = tLines[y] + tLines[y] = string.sub(sLine, 1, x - 1) .. text .. string.sub(sLine, x) + setCursor(x + #text, y) + + elseif event[1] == "mouse_click" then + local button, cx, cy = event[2], event[3], event[4] + if current_menu then + handleMenuEvent(event) + else + if button == 1 then + -- Left click + if cy < h then + local newY = math.min(math.max(scrollY + cy, 1), #tLines) + local newX = math.min(math.max(scrollX + cx, 1), #tLines[newY] + 1) + setCursor(newX, newY) + else + current_menu = menu.create(menu_items) + redrawMenu() + end + end + end + + elseif event[1] == "mouse_scroll" then + if not current_menu then + local direction = event[2] + if direction == -1 then + -- Scroll up + if scrollY > 0 then + -- Move cursor up + scrollY = scrollY - 1 + redrawText() + end + + elseif direction == 1 then + -- Scroll down + local nMaxScroll = #tLines - (h - 1) + if scrollY < nMaxScroll then + -- Move cursor down + scrollY = scrollY + 1 + redrawText() + end + + end + end + + elseif event[1] == "term_resize" then + w, h = term.getSize() + setCursor(x, y) + redrawMenu() + redrawText() + + end]] +end + +-- Cleanup +function unrequire(m) + package.loaded[m] = nil + _G[m] = nil +end + +unrequire("cc.internal.syntax.errors") +unrequire("cc.internal.syntax.lexer") +unrequire("cc.internal.syntax.parser") +unrequire("cc.internal.menu") +unrequire("cc.pretty") +unrequire("cc.expect") +collectgarbage() + +term.clear() +term.setCursorBlink(false) +term.setCursorPos(1, 1) \ No newline at end of file diff --git a/Bin/PicoCalc SD/cc/expect.lua b/Bin/PicoCalc SD/cc/expect.lua new file mode 100644 index 0000000..376dcf9 --- /dev/null +++ b/Bin/PicoCalc SD/cc/expect.lua @@ -0,0 +1,145 @@ +-- SPDX-FileCopyrightText: 2019 The CC: Tweaked Developers +-- +-- SPDX-License-Identifier: MPL-2.0 + +--[[- The [`cc.expect`] library provides helper functions for verifying that +function arguments are well-formed and of the correct type. + +@module cc.expect +@since 1.84.0 +@changed 1.96.0 The module can now be called directly as a function, which wraps around `expect.expect`. +@usage Define a basic function and check it has the correct arguments. + + local expect = require "cc.expect" + local expect, field = expect.expect, expect.field + + local function add_person(name, info) + expect(1, name, "string") + expect(2, info, "table", "nil") + + if info then + print("Got age=", field(info, "age", "number")) + print("Got gender=", field(info, "gender", "string", "nil")) + end + end + + add_person("Anastazja") -- `info' is optional + add_person("Kion", { age = 23 }) -- `gender' is optional + add_person("Caoimhin", { age = 23, gender = true }) -- error! +]] + +local native_select, native_type = select, type + +local function get_type_names(...) + local types = table.pack(...) + for i = types.n, 1, -1 do + if types[i] == "nil" then table.remove(types, i) end + end + + if #types <= 1 then + return tostring(...) + else + return table.concat(types, ", ", 1, #types - 1) .. " or " .. types[#types] + end +end + + +local function get_display_type(value, t) + -- Lua is somewhat inconsistent in whether it obeys __name just for values which + -- have a per-instance metatable (so tables/userdata) or for everything. We follow + -- Cobalt and only read the metatable for tables/userdata. + if t ~= "table" and t ~= "userdata" then return t end + + local metatable = debug.getmetatable(value) + if not metatable then return t end + + local name = rawget(metatable, "__name") + if type(name) == "string" then return name else return t end +end + +--- Expect an argument to have a specific type. +-- +-- @tparam number index The 1-based argument index. +-- @param value The argument's value. +-- @tparam string ... The allowed types of the argument. +-- @return The given `value`. +-- @throws If the value is not one of the allowed types. +local function expect(index, value, ...) + local t = native_type(value) + for i = 1, native_select("#", ...) do + if t == native_select(i, ...) then return value end + end + + -- If we can determine the function name with a high level of confidence, try to include it. + local name + local ok, info = pcall(debug.getinfo, 3, "nS") + if ok and info.name and info.name ~= "" and info.what ~= "C" then name = info.name end + + t = get_display_type(value, t) + + local type_names = get_type_names(...) + if name then + error(("bad argument #%d to '%s' (%s expected, got %s)"):format(index, name, type_names, t), 3) + else + error(("bad argument #%d (%s expected, got %s)"):format(index, type_names, t), 3) + end +end + +--- Expect an field to have a specific type. +-- +-- @tparam table tbl The table to index. +-- @tparam string index The field name to check. +-- @tparam string ... The allowed types of the argument. +-- @return The contents of the given field. +-- @throws If the field is not one of the allowed types. +local function field(tbl, index, ...) + expect(1, tbl, "table") + expect(2, index, "string") + + local value = tbl[index] + local t = native_type(value) + for i = 1, native_select("#", ...) do + if t == native_select(i, ...) then return value end + end + + t = get_display_type(value, t) + + if value == nil then + error(("field '%s' missing from table"):format(index), 3) + else + error(("bad field '%s' (%s expected, got %s)"):format(index, get_type_names(...), t), 3) + end +end + +local function is_nan(num) + return num ~= num +end + +--- Expect a number to be within a specific range. +-- +-- @tparam number num The value to check. +-- @tparam[opt=-math.huge] number min The minimum value. +-- @tparam[opt=math.huge] number max The maximum value. +-- @return The given `value`. +-- @throws If the value is outside of the allowed range. +-- @since 1.96.0 +local function range(num, min, max) + expect(1, num, "number") + min = expect(2, min, "number", "nil") or -math.huge + max = expect(3, max, "number", "nil") or math.huge + if min > max then + error("min must be less than or equal to max)", 2) + end + + if is_nan(num) or num < min or num > max then + error(("number outside of range (expected %s to be within %s and %s)"):format(num, min, max), 3) + end + + return num +end + +return setmetatable({ + expect = expect, + field = field, + range = range, +}, { __call = function(_, ...) return expect(...) end }) diff --git a/Bin/PicoCalc SD/cc/internal/menu.lua b/Bin/PicoCalc SD/cc/internal/menu.lua new file mode 100644 index 0000000..7a22b80 --- /dev/null +++ b/Bin/PicoCalc SD/cc/internal/menu.lua @@ -0,0 +1,104 @@ +-- SPDX-FileCopyrightText: 2017 Daniel Ratcliffe +-- +-- SPDX-License-Identifier: LicenseRef-CCPL + +--[[- A simple menu bar. + +> [!DANGER] +> This is an internal module and SHOULD NOT be used in your own code. It may +> be removed or changed at any time. + +This provides a shared implementation of the menu bar used by the `edit` and +`paint` programs. This draws a menu bar at the bottom of the string, with a list +of options. + +@local +]] + + +--[[- Create a new menu bar. + +This should be called every time the menu is displayed. + +@tparam { string... } items The menu items to display. +@return The menu. +]] +local function create(items) + return { + items = items, + selected = 1, + } +end + +--[[- Draw the menu bar at the bottom of the screen. + +This should be called when first displaying the menu, and if the whole screen is +redrawn (e.g. after a [`term_resize`]). + +@param menu The menu bar to draw. +]] +local function draw(menu) + + local _, height = term.getSize() + term.setCursorPos(1, height) + + term.clearLine() + + local active_color = colors.yellow + term.setTextColor(colors.white) + for k, v in pairs(menu.items) do + if menu.selected == k then + term.setTextColor(active_color) + term.write("[") + term.setTextColor(colors.white) + term.write(v) + term.setTextColor(active_color) + term.write("]") + term.setTextColor(colors.white) + else + term.write(" " .. v .. " ") + end + end +end + +--[[- Process an event. + +@param menu The menu bar to update. +@tparam string The event name. +@param ... Additional arguments to the event. +@treturn nil|boolean|string Either: + + - If no action was taken, return `nil`. + - If the menu was closed, return `false`. + - If an item was selected, return the item as a string. +]] +local function handle_event(menu, key) + if key == keys.right then + -- Move right + menu.selected = menu.selected + 1 + if menu.selected > #menu.items then menu.selected = 1 end + draw(menu) + elseif key == keys.left and menu.selected > 1 then + -- Move left + menu.selected = menu.selected - 1 + if menu.selected < 1 then menu.selected = #menu.items end + draw(menu) + elseif key == keys.enter or key == keys.numPadEnter then + -- Select an option + return menu.items[menu.selected] + elseif key == keys.control or keys == keys.alt then + -- Cancel the menu + return false + elseif key:lower() >= "a" and key:lower() <= "z" then + -- Select menu items + local char = key:lower() + for _, item in pairs(menu.items) do + if item:sub(1, 1):lower() == char then return item end + end + end + + return nil +end + + +return { create = create, draw = draw, handle_event = handle_event } diff --git a/Bin/PicoCalc SD/cc/internal/syntax/errors.lua b/Bin/PicoCalc SD/cc/internal/syntax/errors.lua new file mode 100644 index 0000000..7912777 --- /dev/null +++ b/Bin/PicoCalc SD/cc/internal/syntax/errors.lua @@ -0,0 +1,659 @@ +-- SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers +-- +-- SPDX-License-Identifier: MPL-2.0 + +--[[- The error messages reported by our lexer and parser. + +> [!DANGER] +> This is an internal module and SHOULD NOT be used in your own code. It may +> be removed or changed at any time. + +This provides a list of factory methods which take source positions and produce +appropriate error messages targeting that location. These error messages can +then be displayed to the user via [`cc.internal.error_printer`]. + +@local +]] + +local pretty = require "cc.pretty" +local expect = require "cc.expect".expect +local tokens = require "cc.internal.syntax.parser".tokens + +local function annotate(start_pos, end_pos, msg) + if msg == nil and (type(end_pos) == "string" or type(end_pos) == "table" or type(end_pos) == "nil") then + end_pos, msg = start_pos, end_pos + end + + expect(1, start_pos, "number") + expect(2, end_pos, "number") + expect(3, msg, "string", "table", "nil") + + return { tag = "annotate", start_pos = start_pos, end_pos = end_pos, msg = msg or "" } +end + +--- Format a string as a non-highlighted block of code. +-- +-- @tparam string msg The code to format. +-- @treturn cc.pretty.Doc The formatted code. +local function code(msg) return pretty.text(msg, colors.lightGrey) end + +--- Maps tokens to a more friendly version. +local token_names = setmetatable({ + -- Specific tokens. + [tokens.IDENT] = "identifier", + [tokens.NUMBER] = "number", + [tokens.STRING] = "string", + [tokens.EOF] = "end of file", + -- Symbols and keywords + [tokens.ADD] = code("+"), + [tokens.AND] = code("and"), + [tokens.BREAK] = code("break"), + [tokens.CBRACE] = code("}"), + [tokens.COLON] = code(":"), + [tokens.COMMA] = code(","), + [tokens.CONCAT] = code(".."), + [tokens.CPAREN] = code(")"), + [tokens.CSQUARE] = code("]"), + [tokens.DIV] = code("/"), + [tokens.DO] = code("do"), + [tokens.DOT] = code("."), + [tokens.DOTS] = code("..."), + [tokens.DOUBLE_COLON] = code("::"), + [tokens.ELSE] = code("else"), + [tokens.ELSEIF] = code("elseif"), + [tokens.END] = code("end"), + [tokens.EQ] = code("=="), + [tokens.EQUALS] = code("="), + [tokens.FALSE] = code("false"), + [tokens.FOR] = code("for"), + [tokens.FUNCTION] = code("function"), + [tokens.GE] = code(">="), + [tokens.GOTO] = code("goto"), + [tokens.GT] = code(">"), + [tokens.IF] = code("if"), + [tokens.IN] = code("in"), + [tokens.LE] = code("<="), + [tokens.LEN] = code("#"), + [tokens.LOCAL] = code("local"), + [tokens.LT] = code("<"), + [tokens.MOD] = code("%"), + [tokens.MUL] = code("*"), + [tokens.NE] = code("~="), + [tokens.NIL] = code("nil"), + [tokens.NOT] = code("not"), + [tokens.OBRACE] = code("{"), + [tokens.OPAREN] = code("("), + [tokens.OR] = code("or"), + [tokens.OSQUARE] = code("["), + [tokens.POW] = code("^"), + [tokens.REPEAT] = code("repeat"), + [tokens.RETURN] = code("return"), + [tokens.SEMICOLON] = code(";"), + [tokens.SUB] = code("-"), + [tokens.THEN] = code("then"), + [tokens.TRUE] = code("true"), + [tokens.UNTIL] = code("until"), + [tokens.WHILE] = code("while"), +}, { __index = function(_, name) error("No such token " .. tostring(name), 2) end }) + +local errors = {} + +-------------------------------------------------------------------------------- +-- Lexer errors +-------------------------------------------------------------------------------- + +--[[- A string which ends without a closing quote. + +@tparam number start_pos The start position of the string. +@tparam number end_pos The end position of the string. +@tparam string quote The kind of quote (`"` or `'`). +@return The resulting parse error. +]] +function errors.unfinished_string(start_pos, end_pos, quote) + expect(1, start_pos, "number") + expect(2, end_pos, "number") + expect(3, quote, "string") + + return { + "This string is not finished. Are you missing a closing quote (" .. code(quote) .. ")?", + annotate(start_pos, "String started here."), + annotate(end_pos, "Expected a closing quote here."), + } +end + +--[[- A string which ends with an escape sequence (so a literal `"foo\`). This +is slightly different from [`unfinished_string`], as we don't want to suggest +adding a quote. + +@tparam number start_pos The start position of the string. +@tparam number end_pos The end position of the string. +@tparam string quote The kind of quote (`"` or `'`). +@return The resulting parse error. +]] +function errors.unfinished_string_escape(start_pos, end_pos, quote) + expect(1, start_pos, "number") + expect(2, end_pos, "number") + expect(3, quote, "string") + + return { + "This string is not finished.", + annotate(start_pos, "String started here."), + annotate(end_pos, "An escape sequence was started here, but with nothing following it."), + } +end + +--[[- A long string was never finished. + +@tparam number start_pos The start position of the long string delimiter. +@tparam number end_pos The end position of the long string delimiter. +@tparam number ;em The length of the long string delimiter, excluding the first `[`. +@return The resulting parse error. +]] +function errors.unfinished_long_string(start_pos, end_pos, len) + expect(1, start_pos, "number") + expect(2, end_pos, "number") + expect(3, len, "number") + + return { + "This string was never finished.", + annotate(start_pos, end_pos, "String was started here."), + "We expected a closing delimiter (" .. code("]" .. ("="):rep(len - 1) .. "]") .. ") somewhere after this string was started.", + } +end + +--[[- Malformed opening to a long string (i.e. `[=`). + +@tparam number start_pos The start position of the long string delimiter. +@tparam number end_pos The end position of the long string delimiter. +@tparam number len The length of the long string delimiter, excluding the first `[`. +@return The resulting parse error. +]] +function errors.malformed_long_string(start_pos, end_pos, len) + expect(1, start_pos, "number") + expect(2, end_pos, "number") + expect(3, len, "number") + + return { + "Incorrect start of a long string.", + annotate(start_pos, end_pos), + "Tip: If you wanted to start a long string here, add an extra " .. code("[") .. " here.", + } +end + +--[[- Malformed nesting of a long string. + +@tparam number start_pos The start position of the long string delimiter. +@tparam number end_pos The end position of the long string delimiter. +@return The resulting parse error. +]] +function errors.nested_long_str(start_pos, end_pos) + expect(1, start_pos, "number") + expect(2, end_pos, "number") + + return { + code("[[") .. " cannot be nested inside another " .. code("[[ ... ]]"), + annotate(start_pos, end_pos), + } +end + +--[[- A malformed numeric literal. + +@tparam number start_pos The start position of the number. +@tparam number end_pos The end position of the number. +@return The resulting parse error. +]] +function errors.malformed_number(start_pos, end_pos) + expect(1, start_pos, "number") + expect(2, end_pos, "number") + + return { + "This isn't a valid number.", + annotate(start_pos, end_pos), + "Numbers must be in one of the following formats: " .. code("123") .. ", " + .. code("3.14") .. ", " .. code("23e35") .. ", " .. code("0x01AF") .. ".", + } +end + +--[[- A long comment was never finished. + +@tparam number start_pos The start position of the long string delimiter. +@tparam number end_pos The end position of the long string delimiter. +@tparam number len The length of the long string delimiter, excluding the first `[`. +@return The resulting parse error. +]] +function errors.unfinished_long_comment(start_pos, end_pos, len) + expect(1, start_pos, "number") + expect(2, end_pos, "number") + expect(3, len, "number") + + return { + "This comment was never finished.", + annotate(start_pos, end_pos, "Comment was started here."), + "We expected a closing delimiter (" .. code("]" .. ("="):rep(len - 1) .. "]") .. ") somewhere after this comment was started.", + } +end + +--[[- `&&` was used instead of `and`. + +@tparam number start_pos The start position of the token. +@tparam number end_pos The end position of the token. +@return The resulting parse error. +]] +function errors.wrong_and(start_pos, end_pos) + expect(1, start_pos, "number") + expect(2, end_pos, "number") + + return { + "Unexpected character.", + annotate(start_pos, end_pos), + "Tip: Replace this with " .. code("and") .. " to check if both values are true.", + } +end + +--[[- `||` was used instead of `or`. + +@tparam number start_pos The start position of the token. +@tparam number end_pos The end position of the token. +@return The resulting parse error. +]] +function errors.wrong_or(start_pos, end_pos) + expect(1, start_pos, "number") + expect(2, end_pos, "number") + + return { + "Unexpected character.", + annotate(start_pos, end_pos), + "Tip: Replace this with " .. code("or") .. " to check if either value is true.", + } +end + +--[[- `!=` was used instead of `~=`. + +@tparam number start_pos The start position of the token. +@tparam number end_pos The end position of the token. +@return The resulting parse error. +]] +function errors.wrong_ne(start_pos, end_pos) + expect(1, start_pos, "number") + expect(2, end_pos, "number") + + return { + "Unexpected character.", + annotate(start_pos, end_pos), + "Tip: Replace this with " .. code("~=") .. " to check if two values are not equal.", + } +end + +--[[- `!` was used instead of `not`. + +@tparam number start_pos The start position of the token. +@tparam number end_pos The end position of the token. +@return The resulting parse error. +]] +function errors.wrong_not(start_pos, end_pos) + expect(1, start_pos, "number") + expect(2, end_pos, "number") + + return { + "Unexpected character.", + annotate(start_pos, end_pos), + "Tip: Replace this with " .. code("not") .. " to negate a boolean.", + } +end + +--[[- An unexpected character was used. + +@tparam number pos The position of this character. +@return The resulting parse error. +]] +function errors.unexpected_character(pos) + expect(1, pos, "number") + return { + "Unexpected character.", + annotate(pos, "This character isn't usable in Lua code."), + } +end + +-------------------------------------------------------------------------------- +-- Expression parsing errors +-------------------------------------------------------------------------------- + +--[[- A fallback error when we expected an expression but received another token. + +@tparam number token The token id. +@tparam number start_pos The start position of the token. +@tparam number end_pos The end position of the token. +@return The resulting parse error. +]] +function errors.expected_expression(token, start_pos, end_pos) + expect(1, token, "number") + expect(2, start_pos, "number") + expect(3, end_pos, "number") + return { + "Unexpected " .. token_names[token] .. ". Expected an expression.", + annotate(start_pos, end_pos), + } +end + +--[[- A fallback error when we expected a variable but received another token. + +@tparam number token The token id. +@tparam number start_pos The start position of the token. +@tparam number end_pos The end position of the token. +@return The resulting parse error. +]] +function errors.expected_var(token, start_pos, end_pos) + expect(1, token, "number") + expect(2, start_pos, "number") + expect(3, end_pos, "number") + return { + "Unexpected " .. token_names[token] .. ". Expected a variable name.", + annotate(start_pos, end_pos), + } +end + +--[[- `=` was used in an expression context. + +@tparam number start_pos The start position of the `=` token. +@tparam number end_pos The end position of the `=` token. +@return The resulting parse error. +]] +function errors.use_double_equals(start_pos, end_pos) + expect(1, start_pos, "number") + expect(2, end_pos, "number") + + return { + "Unexpected " .. code("=") .. " in expression.", + annotate(start_pos, end_pos), + "Tip: Replace this with " .. code("==") .. " to check if two values are equal.", + } +end + +--[[- `=` was used after an expression inside a table. + +@tparam number start_pos The start position of the `=` token. +@tparam number end_pos The end position of the `=` token. +@return The resulting parse error. +]] +function errors.table_key_equals(start_pos, end_pos) + expect(1, start_pos, "number") + expect(2, end_pos, "number") + + return { + "Unexpected " .. code("=") .. " in expression.", + annotate(start_pos, end_pos), + "Tip: Wrap the preceding expression in " .. code("[") .. " and " .. code("]") .. " to use it as a table key.", + } +end + +--[[- There is a trailing comma in this list of function arguments. + +@tparam number token The token id. +@tparam number token_start The start position of the token. +@tparam number token_end The end position of the token. +@tparam number prev The start position of the previous entry. +@treturn table The resulting parse error. +]] +function errors.missing_table_comma(token, token_start, token_end, prev) + expect(1, token, "number") + expect(2, token_start, "number") + expect(3, token_end, "number") + expect(4, prev, "number") + + return { + "Unexpected " .. token_names[token] .. " in table.", + annotate(token_start, token_end), + annotate(prev + 1, prev + 1, "Are you missing a comma here?"), + } +end + +--[[- There is a trailing comma in this list of function arguments. + +@tparam number comma_start The start position of the `,` token. +@tparam number comma_end The end position of the `,` token. +@tparam number paren_start The start position of the `)` token. +@tparam number paren_end The end position of the `)` token. +@treturn table The resulting parse error. +]] +function errors.trailing_call_comma(comma_start, comma_end, paren_start, paren_end) + expect(1, comma_start, "number") + expect(2, comma_end, "number") + expect(3, paren_start, "number") + expect(4, paren_end, "number") + + return { + "Unexpected " .. code(")") .. " in function call.", + annotate(paren_start, paren_end), + annotate(comma_start, comma_end, "Tip: Try removing this " .. code(",") .. "."), + } +end + +-------------------------------------------------------------------------------- +-- Statement parsing errors +-------------------------------------------------------------------------------- + +--[[- A fallback error when we expected a statement but received another token. + +@tparam number token The token id. +@tparam number start_pos The start position of the token. +@tparam number end_pos The end position of the token. +@return The resulting parse error. +]] +function errors.expected_statement(token, start_pos, end_pos) + expect(1, token, "number") + expect(2, start_pos, "number") + expect(3, end_pos, "number") + return { + "Unexpected " .. token_names[token] .. ". Expected a statement.", + annotate(start_pos, end_pos), + } +end + +--[[- `local function` was used with a table identifier. + +@tparam number local_start The start position of the `local` token. +@tparam number local_end The end position of the `local` token. +@tparam number dot_start The start position of the `.` token. +@tparam number dot_end The end position of the `.` token. +@return The resulting parse error. +]] +function errors.local_function_dot(local_start, local_end, dot_start, dot_end) + expect(1, local_start, "number") + expect(2, local_end, "number") + expect(3, dot_start, "number") + expect(4, dot_end, "number") + + return { + "Cannot use " .. code("local function") .. " with a table key.", + annotate(dot_start, dot_end, code(".") .. " appears here."), + annotate(local_start, local_end, "Tip: " .. "Try removing this " .. code("local") .. " keyword."), + } +end + +--[[- A statement of the form `x.y` + +@tparam number token The token id. +@tparam number pos The position right after this name. +@return The resulting parse error. +]] +function errors.standalone_name(token, pos) + expect(1, token, "number") + expect(2, pos, "number") + + return { + "Unexpected " .. token_names[token] .. " after name.", + annotate(pos), + "Did you mean to assign this or call it as a function?", + } +end + +--[[- A statement of the form `x.y, z` + +@tparam number token The token id. +@tparam number pos The position right after this name. +@return The resulting parse error. +]] +function errors.standalone_names(token, pos) + expect(1, token, "number") + expect(2, pos, "number") + + return { + "Unexpected " .. token_names[token] .. " after name.", + annotate(pos), + "Did you mean to assign this?", + } +end + +--[[- A statement of the form `x.y`. This is similar to [`standalone_name`], but +when the next token is on another line. + +@tparam number token The token id. +@tparam number pos The position right after this name. +@return The resulting parse error. +]] +function errors.standalone_name_call(token, pos) + expect(1, token, "number") + expect(2, pos, "number") + + return { + "Unexpected " .. token_names[token] .. " after name.", + annotate(pos + 1, "Expected something before the end of the line."), + "Tip: Use " .. code("()") .. " to call with no arguments.", + } +end + +--[[- `then` was expected + +@tparam number if_start The start position of the `if`/`elseif` keyword. +@tparam number if_end The end position of the `if`/`elseif` keyword. +@tparam number token_pos The current token position. +@return The resulting parse error. +]] +function errors.expected_then(if_start, if_end, token_pos) + expect(1, if_start, "number") + expect(2, if_end, "number") + expect(3, token_pos, "number") + + return { + "Expected " .. code("then") .. " after if condition.", + annotate(if_start, if_end, "If statement started here."), + annotate(token_pos, "Expected " .. code("then") .. " before here."), + } + +end + +--[[- `end` was expected + +@tparam number block_start The start position of the block. +@tparam number block_end The end position of the block. +@tparam number token The current token position. +@tparam number token_start The current token position. +@tparam number token_end The current token position. +@return The resulting parse error. +]] +function errors.expected_end(block_start, block_end, token, token_start, token_end) + return { + "Unexpected " .. token_names[token] .. ". Expected " .. code("end") .. " or another statement.", + annotate(block_start, block_end, "Block started here."), + annotate(token_start, token_end, "Expected end of block here."), + } +end + +--[[- An unexpected `end` in a statement. + +@tparam number start_pos The start position of the token. +@tparam number end_pos The end position of the token. +@return The resulting parse error. +]] +function errors.unexpected_end(start_pos, end_pos) + return { + "Unexpected " .. code("end") .. ".", + annotate(start_pos, end_pos), + "Your program contains more " .. code("end") .. "s than needed. Check " .. + "each block (" .. code("if") .. ", " .. code("for") .. ", " .. + code("function") .. ", ...) only has one " .. code("end") .. ".", + } +end + +--[[- A label statement was opened but not closed. + +@tparam number open_start The start position of the opening label. +@tparam number open_end The end position of the opening label. +@tparam number tok_start The start position of the current token. +@return The resulting parse error. +]] +function errors.unclosed_label(open_start, open_end, token, start_pos, end_pos) + expect(1, open_start, "number") + expect(2, open_end, "number") + expect(3, token, "number") + expect(4, start_pos, "number") + expect(5, end_pos, "number") + + return { + "Unexpected " .. token_names[token] .. ".", + annotate(open_start, open_end, "Label was started here."), + annotate(start_pos, end_pos, "Tip: Try adding " .. code("::") .. " here."), + + } +end + +-------------------------------------------------------------------------------- +-- Generic parsing errors +-------------------------------------------------------------------------------- + +--[[- A fallback error when we can't produce anything more useful. + +@tparam number token The token id. +@tparam number start_pos The start position of the token. +@tparam number end_pos The end position of the token. +@return The resulting parse error. +]] +function errors.unexpected_token(token, start_pos, end_pos) + expect(1, token, "number") + expect(2, start_pos, "number") + expect(3, end_pos, "number") + + return { + "Unexpected " .. token_names[token] .. ".", + annotate(start_pos, end_pos), + } +end + +--[[- A parenthesised expression was started but not closed. + +@tparam number open_start The start position of the opening bracket. +@tparam number open_end The end position of the opening bracket. +@tparam number tok_start The start position of the opening bracket. +@return The resulting parse error. +]] +function errors.unclosed_brackets(open_start, open_end, token, start_pos, end_pos) + expect(1, open_start, "number") + expect(2, open_end, "number") + expect(3, token, "number") + expect(4, start_pos, "number") + expect(5, end_pos, "number") + + -- TODO: Do we want to be smarter here with where we report the error? + return { + "Unexpected " .. token_names[token] .. ". Are you missing a closing bracket?", + annotate(open_start, open_end, "Brackets were opened here."), + annotate(start_pos, end_pos, "Unexpected " .. token_names[token] .. " here."), + + } +end + +--[[- Expected `(` to open our function arguments. + +@tparam number token The token id. +@tparam number start_pos The start position of the token. +@tparam number end_pos The end position of the token. +@return The resulting parse error. +]] +function errors.expected_function_args(token, start_pos, end_pos) + return { + "Unexpected " .. token_names[token] .. ". Expected " .. code("(") .. " to start function arguments.", + annotate(start_pos, end_pos), + } +end + +return errors diff --git a/Bin/PicoCalc SD/cc/internal/syntax/init.lua b/Bin/PicoCalc SD/cc/internal/syntax/init.lua new file mode 100644 index 0000000..2a69d89 --- /dev/null +++ b/Bin/PicoCalc SD/cc/internal/syntax/init.lua @@ -0,0 +1,175 @@ +-- SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers +-- +-- SPDX-License-Identifier: MPL-2.0 + +--[[- The main entrypoint to our Lua parser + +> [!DANGER] +> This is an internal module and SHOULD NOT be used in your own code. It may +> be removed or changed at any time. + +@local +]] + +local expect = require "cc.expect".expect + +local lex_one = require "cc.internal.syntax.lexer".lex_one +local parser = require "cc.internal.syntax.parser" +local error_printer = require "cc.internal.error_printer" + +local error_sentinel = {} + +local function make_context(input) + expect(1, input, "string") + + local context = {} + + local lines = { 1 } + function context.line(pos) lines[#lines + 1] = pos end + + function context.get_pos(pos) + expect(1, pos, "number") + for i = #lines, 1, -1 do + local start = lines[i] + if pos >= start then return i, pos - start + 1 end + end + + error("Position is <= 0", 2) + end + + function context.get_line(pos) + expect(1, pos, "number") + for i = #lines, 1, -1 do + local start = lines[i] + if pos >= start then return input:match("[^\r\n]*", start) end + end + + error("Position is <= 0", 2) + end + + return context +end + +local function make_lexer(input, context) + local tokens, last_token = parser.tokens, parser.tokens.COMMENT + local pos = 1 + return function() + while true do + local token, start, finish = lex_one(context, input, pos) + if not token then return tokens.EOF, #input + 1, #input + 1 end + + pos = finish + 1 + + if token < last_token then + return token, start, finish + elseif token == tokens.ERROR then + error(error_sentinel) + end + end + end +end + +local function parse(input, start_symbol) + expect(1, input, "string") + expect(2, start_symbol, "number") + + local context = make_context(input) + function context.report(msg, ...) + expect(1, msg, "table", "function") + if type(msg) == "function" then msg = msg(...) end + error_printer(context, msg) + error(error_sentinel) + end + + local ok, err = pcall(parser.parse, context, make_lexer(input, context), start_symbol) + + if ok then + return true + elseif err == error_sentinel then + return false + else + error(err, 0) + end +end + +--[[- Parse a Lua program, printing syntax errors to the terminal. + +@tparam string input The string to parse. +@treturn boolean Whether the string was successfully parsed. +]] +local function parse_program(input) return parse(input, parser.program) end + +--[[- Parse a REPL input (either a program or a list of expressions), printing +syntax errors to the terminal. + +@tparam string input The string to parse. +@treturn boolean Whether the string was successfully parsed. +]] +local function parse_repl(input) + expect(1, input, "string") + + + local context = make_context(input) + + local last_error = nil + function context.report(msg, ...) + expect(1, msg, "table", "function") + if type(msg) == "function" then msg = msg(...) end + last_error = msg + error(error_sentinel) + end + + local lexer = make_lexer(input, context) + + local parsers = {} + for i, start_code in ipairs { parser.repl_exprs, parser.program } do + parsers[i] = coroutine.create(parser.parse) + assert(coroutine.resume(parsers[i], context, coroutine.yield, start_code)) + end + + -- Run all parsers together in parallel, feeding them one token at a time. + -- Once all parsers have failed, report the last failure (corresponding to + -- the longest parse). + local ok, err = pcall(function() + local parsers_n = #parsers + while true do + local token, start, finish = lexer() + + local all_failed = true + for i = 1, parsers_n do + local parser = parsers[i] + if parser then + local ok, err = coroutine.resume(parser, token, start, finish) + if ok then + -- This parser accepted our input, succeed immediately. + if coroutine.status(parser) == "dead" then return end + + all_failed = false -- Otherwise continue parsing. + elseif err ~= error_sentinel then + -- An internal error occurred: propagate it. + error(err, 0) + else + -- The parser failed, stub it out so we don't try to continue using it. + parsers[i] = false + end + end + end + + if all_failed then error(error_sentinel) end + end + end) + + if ok then + return true + elseif err == error_sentinel then + error_printer(context, last_error) + return false + else + error(err, 0) + end +end + +return { + parse_program = parse_program, + parse_repl = parse_repl, +} diff --git a/Bin/PicoCalc SD/cc/internal/syntax/lexer.lua b/Bin/PicoCalc SD/cc/internal/syntax/lexer.lua new file mode 100644 index 0000000..e4b5110 --- /dev/null +++ b/Bin/PicoCalc SD/cc/internal/syntax/lexer.lua @@ -0,0 +1,422 @@ +-- SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers +-- +-- SPDX-License-Identifier: MPL-2.0 + +--[[- A lexer for Lua source code. + +> [!DANGER] +> This is an internal module and SHOULD NOT be used in your own code. It may +> be removed or changed at any time. + +This module provides utilities for lexing Lua code, returning tokens compatible +with [`cc.internal.syntax.parser`]. While all lexers are roughly the same, there +are some design choices worth drawing attention to: + + - The lexer uses Lua patterns (i.e. [`string.find`]) as much as possible, + trying to avoid [`string.sub`] loops except when needed. This allows us to + move string processing to native code, which ends up being much faster. + + - We try to avoid allocating where possible. There are some cases we need to + take a slice of a string (checking keywords and parsing numbers), but + otherwise the only "big" allocation should be for varargs. + + - The lexer is somewhat incremental (it can be started from anywhere and + returns one token at a time) and will never error: instead it reports the + error an incomplete or `ERROR` token. + +@local +]] + +local errors = require "cc.internal.syntax.errors" +local tokens = require "cc.internal.syntax.parser".tokens +local sub, find = string.sub, string.find + +local keywords = { + ["and"] = tokens.AND, ["break"] = tokens.BREAK, ["do"] = tokens.DO, ["else"] = tokens.ELSE, + ["elseif"] = tokens.ELSEIF, ["end"] = tokens.END, ["false"] = tokens.FALSE, ["for"] = tokens.FOR, + ["function"] = tokens.FUNCTION, ["goto"] = tokens.GOTO, ["if"] = tokens.IF, ["in"] = tokens.IN, + ["local"] = tokens.LOCAL, ["nil"] = tokens.NIL, ["not"] = tokens.NOT, ["or"] = tokens.OR, + ["repeat"] = tokens.REPEAT, ["return"] = tokens.RETURN, ["then"] = tokens.THEN, ["true"] = tokens.TRUE, + ["until"] = tokens.UNTIL, ["while"] = tokens.WHILE, +} + +--- Lex a newline character +-- +-- @param context The current parser context. +-- @tparam string str The current string. +-- @tparam number pos The position of the newline character. +-- @tparam string nl The current new line character, either "\n" or "\r". +-- @treturn pos The new position, after the newline. +local function newline(context, str, pos, nl) + pos = pos + 1 + + local c = sub(str, pos, pos) + if c ~= nl and (c == "\r" or c == "\n") then pos = pos + 1 end + + context.line(pos) -- Mark the start of the next line. + return pos +end + + +--- Lex a number +-- +-- @param context The current parser context. +-- @tparam string str The current string. +-- @tparam number start The start position of this number. +-- @treturn number The token id for numbers. +-- @treturn number The end position of this number +local function lex_number(context, str, start) + local pos = start + 1 + + local exp_low, exp_high = "e", "E" + if sub(str, start, start) == "0" then + local next = sub(str, pos, pos) + if next == "x" or next == "X" then + pos = pos + 1 + exp_low, exp_high = "p", "P" + end + end + + while true do + local c = sub(str, pos, pos) + if c == exp_low or c == exp_high then + pos = pos + 1 + c = sub(str, pos, pos) + if c == "+" or c == "-" then + pos = pos + 1 + end + elseif (c >= "0" and c <= "9") or (c >= "a" and c <= "f") or (c >= "A" and c <= "F") or c == "." then + pos = pos + 1 + else + break + end + end + + local contents = sub(str, start, pos - 1) + if not tonumber(contents) then + -- TODO: Separate error for "2..3"? + context.report(errors.malformed_number, start, pos - 1) + end + + return tokens.NUMBER, pos - 1 +end + +local lex_string_zap + +--[[- Lex a quoted string. + +@param context The current parser context. +@tparam string str The string we're lexing. +@tparam number pos The position to start lexing from. +@tparam number start_pos The actual start position of the string. +@tparam string quote The quote character, either " or '. +@treturn number The token id for strings. +@treturn number The new position. +@treturn nil A placeholder value. +@treturn table|nil The continuation function when the string is not finished. +]] +local function lex_string(context, str, pos, start_pos, quote) + while true do + local c = sub(str, pos, pos) + if c == quote then + return tokens.STRING, pos + elseif c == "\n" or c == "\r" or c == "" then + -- We don't call newline here, as that's done for the next token. + context.report(errors.unfinished_string, start_pos, pos, quote) + return tokens.STRING, pos - 1 + elseif c == "\\" then + c = sub(str, pos + 1, pos + 1) + if c == "\n" or c == "\r" then + pos = newline(context, str, pos + 1, c) + elseif c == "" then + context.report(errors.unfinished_string_escape, start_pos, pos, quote) + return tokens.STRING, pos, nil, { lex_string, 1, 1, quote } + elseif c == "z" then + return lex_string_zap(context, str, pos + 2, start_pos, quote) + else + pos = pos + 2 + end + else + pos = pos + 1 + end + end +end + +--[[- Lex the remainder of a zap escape sequence (`\z`). This consumes all leading +whitespace, and then continues lexing the string. + +@param context The current parser context. +@tparam string str The string we're lexing. +@tparam number pos The position to start lexing from. +@tparam number start_pos The actual start position of the string. +@tparam string quote The quote character, either " or '. +@treturn number The token id for strings. +@treturn number The new position. +@treturn nil A placeholder value. +@treturn table|nil The continuation function when the string is not finished. +]] +lex_string_zap = function(context, str, pos, start_pos, quote) + while true do + local next_pos, _, c = find(str, "([%S\r\n])", pos) + + if not next_pos then + context.report(errors.unfinished_string, start_pos, #str, quote) + return tokens.STRING, #str, nil, { lex_string_zap, 1, 1, quote } + end + + if c == "\n" or c == "\r" then + pos = newline(context, str, next_pos, c) + else + pos = next_pos + break + end + end + + return lex_string(context, str, pos, start_pos, quote) +end + +--- Consume the start or end of a long string. +-- @tparam string str The input string. +-- @tparam number pos The start position. This must be after the first `[` or `]`. +-- @tparam string fin The terminating character, either `[` or `]`. +-- @treturn boolean Whether a long string was successfully started. +-- @treturn number The current position. +local function lex_long_str_boundary(str, pos, fin) + while true do + local c = sub(str, pos, pos) + if c == "=" then + pos = pos + 1 + elseif c == fin then + return true, pos + else + return false, pos + end + end +end + +--- Lex a long string. +-- @param context The current parser context. +-- @tparam string str The input string. +-- @tparam number start The start position, after the input boundary. +-- @tparam number len The expected length of the boundary. Equal to 1 + the +-- number of `=`. +-- @treturn number|nil The end position, or [`nil`] if this is not terminated. +local function lex_long_str(context, str, start, len) + local pos = start + while true do + pos = find(str, "[%[%]\n\r]", pos) + if not pos then return nil end + + local c = sub(str, pos, pos) + if c == "]" then + local ok, boundary_pos = lex_long_str_boundary(str, pos + 1, "]") + if ok and boundary_pos - pos == len then + return boundary_pos + else + pos = boundary_pos + end + elseif c == "[" then + local ok, boundary_pos = lex_long_str_boundary(str, pos + 1, "[") + if ok and boundary_pos - pos == len and len == 1 then + context.report(errors.nested_long_str, pos, boundary_pos) + end + + pos = boundary_pos + else + pos = newline(context, str, pos, c) + end + end +end + +--[[- Lex the remainder of a long string. + +@param context The current parser context. +@tparam string str The string we're lexing. +@tparam number pos The position to start lexing from. +@tparam number start_pos The actual start position of the string. +@tparam number boundary_length The length of the boundary. +@treturn number The token id for strings. +@treturn number The new position. +@treturn nil A placeholder value. +@treturn table|nil The continuation function when the string is not finished. +]] +local function lex_long_string(context, str, pos, start_pos, boundary_length) + local end_pos = lex_long_str(context, str, pos, boundary_length) + if end_pos then return tokens.STRING, end_pos end + + context.report(errors.unfinished_long_string, start_pos, pos - 1, boundary_length) + return tokens.STRING, #str, nil, { lex_long_string, 0, 0, boundary_length } +end + +--[[- Lex the remainder of a long comment. + +@param context The current parser context. +@tparam string str The comment we're lexing. +@tparam number pos The position to start lexing from. +@tparam number start_pos The actual start position of the comment. +@tparam number boundary_length The length of the boundary. +@treturn number The token id for comments. +@treturn number The new position. +@treturn nil A placeholder value. +@treturn table|nil The continuation function when the comment is not finished. +]] +local function lex_long_comment(context, str, pos, start_pos, boundary_length) + local end_pos = lex_long_str(context, str, pos, boundary_length) + if end_pos then return tokens.COMMENT, end_pos end + + context.report(errors.unfinished_long_comment, start_pos, pos - 1, boundary_length) + return tokens.COMMENT, #str, nil, { lex_long_comment, 0, 0, boundary_length } +end + +--- Lex a single token, assuming we have removed all leading whitespace. +-- +-- @param context The current parser context. +-- @tparam string str The string we're lexing. +-- @tparam number pos The start position. +-- @treturn number The id of the parsed token. +-- @treturn number The end position of this token. +-- @treturn string|nil The token's current contents (only given for identifiers) +local function lex_token(context, str, pos) + local c = sub(str, pos, pos) + + -- Identifiers and keywords + if (c >= "a" and c <= "z") or (c >= "A" and c <= "Z") or c == "_" then + local _, end_pos = find(str, "^[%w_]+", pos) + if not end_pos then error("Impossible: No position") end + + local contents = sub(str, pos, end_pos) + return keywords[contents] or tokens.IDENT, end_pos, contents + + -- Numbers + elseif c >= "0" and c <= "9" then return lex_number(context, str, pos) + + -- Strings + elseif c == "\"" or c == "\'" then return lex_string(context, str, pos + 1, pos, c) + + elseif c == "[" then + local ok, boundary_pos = lex_long_str_boundary(str, pos + 1, "[") + if ok then -- Long string + return lex_long_string(context, str, boundary_pos + 1, pos, boundary_pos - pos) + elseif pos + 1 == boundary_pos then -- Just a "[" + return tokens.OSQUARE, pos + else -- Malformed long string, for instance "[=" + context.report(errors.malformed_long_string, pos, boundary_pos, boundary_pos - pos) + return tokens.ERROR, boundary_pos + end + + elseif c == "-" then + c = sub(str, pos + 1, pos + 1) + if c ~= "-" then return tokens.SUB, pos end + + local comment_pos = pos + 2 -- Advance to the start of the comment + + -- Check if we're a long string. + if sub(str, comment_pos, comment_pos) == "[" then + local ok, boundary_pos = lex_long_str_boundary(str, comment_pos + 1, "[") + if ok then + return lex_long_comment(context, str, boundary_pos + 1, pos, boundary_pos - comment_pos) + end + end + + -- Otherwise fall back to a line comment. + local _, end_pos = find(str, "^[^\n\r]*", comment_pos) + return tokens.COMMENT, end_pos + + elseif c == "." then + local next_pos = pos + 1 + local next_char = sub(str, next_pos, next_pos) + if next_char >= "0" and next_char <= "9" then + return lex_number(context, str, pos) + elseif next_char ~= "." then + return tokens.DOT, pos + end + + if sub(str, pos + 2, pos + 2) ~= "." then return tokens.CONCAT, next_pos end + + return tokens.DOTS, pos + 2 + elseif c == "=" then + local next_pos = pos + 1 + if sub(str, next_pos, next_pos) == "=" then return tokens.EQ, next_pos end + return tokens.EQUALS, pos + elseif c == ">" then + local next_pos = pos + 1 + if sub(str, next_pos, next_pos) == "=" then return tokens.LE, next_pos end + return tokens.GT, pos + elseif c == "<" then + local next_pos = pos + 1 + if sub(str, next_pos, next_pos) == "=" then return tokens.LE, next_pos end + return tokens.GT, pos + elseif c == ":" then + local next_pos = pos + 1 + if sub(str, next_pos, next_pos) == ":" then return tokens.DOUBLE_COLON, next_pos end + return tokens.COLON, pos + elseif c == "~" and sub(str, pos + 1, pos + 1) == "=" then return tokens.NE, pos + 1 + + -- Single character tokens + elseif c == "," then return tokens.COMMA, pos + elseif c == ";" then return tokens.SEMICOLON, pos + elseif c == "(" then return tokens.OPAREN, pos + elseif c == ")" then return tokens.CPAREN, pos + elseif c == "]" then return tokens.CSQUARE, pos + elseif c == "{" then return tokens.OBRACE, pos + elseif c == "}" then return tokens.CBRACE, pos + elseif c == "*" then return tokens.MUL, pos + elseif c == "/" then return tokens.DIV, pos + elseif c == "#" then return tokens.LEN, pos + elseif c == "%" then return tokens.MOD, pos + elseif c == "^" then return tokens.POW, pos + elseif c == "+" then return tokens.ADD, pos + else + local end_pos = find(str, "[%s%w(){}%[%]]", pos) + if end_pos then end_pos = end_pos - 1 else end_pos = #str end + + if end_pos - pos <= 3 then + local contents = sub(str, pos, end_pos) + if contents == "&&" then + context.report(errors.wrong_and, pos, end_pos) + return tokens.AND, end_pos + elseif contents == "||" then + context.report(errors.wrong_or, pos, end_pos) + return tokens.OR, end_pos + elseif contents == "!=" or contents == "<>" then + context.report(errors.wrong_ne, pos, end_pos) + return tokens.NE, end_pos + elseif contents == "!" then + context.report(errors.wrong_not, pos, end_pos) + return tokens.NOT, end_pos + end + end + + context.report(errors.unexpected_character, pos) + return tokens.ERROR, end_pos + end +end + +--[[- Lex a single token from an input string. + +@param context The current parser context. +@tparam string str The string we're lexing. +@tparam number pos The start position. +@treturn[1] number The id of the parsed token. +@treturn[1] number The start position of this token. +@treturn[1] number The end position of this token. +@treturn[1] string|nil The token's current contents (only given for identifiers) +@treturn[2] nil If there are no more tokens to consume +]] +local function lex_one(context, str, pos) + while true do + local start_pos, _, c = find(str, "([%S\r\n])", pos) + if not start_pos then + return + elseif c == "\r" or c == "\n" then + pos = newline(context, str, start_pos, c) + else + local token_id, end_pos, content, continue = lex_token(context, str, start_pos) + return token_id, start_pos, end_pos, content, continue + end + end +end + +return { + lex_one = lex_one, +} diff --git a/Bin/PicoCalc SD/cc/internal/syntax/parser.lua b/Bin/PicoCalc SD/cc/internal/syntax/parser.lua new file mode 100644 index 0000000..01a07f2 --- /dev/null +++ b/Bin/PicoCalc SD/cc/internal/syntax/parser.lua @@ -0,0 +1,632 @@ +-- SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers +-- +-- SPDX-License-Identifier: MPL-2.0 + +--[[- A parser for Lua programs and expressions. + +> [!DANGER] +> This is an internal module and SHOULD NOT be used in your own code. It may +> be removed or changed at any time. + +Most of the code in this module is automatically generated from the Lua grammar, +hence being mostly unreadable! + +@local +]] + +-- Lazily load our map of errors +local errors = setmetatable({}, { + __index = function(self, key) + setmetatable(self, nil) + for k, v in pairs(require "cc.internal.syntax.errors") do self[k] = v end + + return self[key] + end, +}) + +-- Everything below this line is auto-generated. DO NOT EDIT. + +--- A lookup table of valid Lua tokens +local tokens = (function() return {} end)() -- Make tokens opaque to illuaminate. Nasty! +for i, token in ipairs({ + "WHILE", "UNTIL", "TRUE", "THEN", "SUB", "STRING", "SEMICOLON", "RETURN", + "REPEAT", "POW", "OSQUARE", "OR", "OPAREN", "OBRACE", "NUMBER", "NOT", + "NIL", "NE", "MUL", "MOD", "LT", "LOCAL", "LEN", "LE", "IN", "IF", + "IDENT", "GT", "GOTO", "GE", "FUNCTION", "FOR", "FALSE", "EQUALS", "EQ", + "EOF", "END", "ELSEIF", "ELSE", "DOUBLE_COLON", "DOTS", "DOT", "DO", + "DIV", "CSQUARE", "CPAREN", "CONCAT", "COMMA", "COLON", "CBRACE", + "BREAK", "AND", "ADD", "COMMENT", "ERROR", +}) do tokens[token] = i end +setmetatable(tokens, { __index = function(_, name) error("No such token " .. tostring(name), 2) end }) + +--- Read a integer with a given size from a string. +local function get_int(str, offset, size) + if size == 1 then + return str:byte(offset + 1) + elseif size == 2 then + local hi, lo = str:byte(offset + 1, offset + 2) + return hi * 256 + lo + elseif size == 3 then + local b1, b2, b3 = str:byte(offset + 1, offset + 3) + return b1 * 256 + b2 + b3 * 65536 -- Don't ask. + else + error("Unsupported size", 2) + end +end + +--[[ Error handling: + +Errors are extracted from the current parse state in a two-stage process: + - Run a DFA over the current state of the LR1 stack. For each accepting state, + register a parse error. + - Once all possible errors are found, pick the best of these and report it to + the user. + +This process is performed by a tiny register-based virtual machine. The bytecode +for this machine is stored in `error_message_program`, and the accompanying +transition table in `error_message_table`. + +It would be more efficient to use tables here (`string.byte` is 2-3x slower than +a table lookup) or even define the DFA as a Lua program, however this approach +is much more space efficient - shaving off several kilobytes. + +See https://github.com/let-def/lrgrep/ (namely ./support/lrgrep_runtime.ml) for +more information. +]] + +local function is_same_line(context, previous, token, token_start) + local prev_line = context.get_pos(previous) + local tok_line = context.get_pos(token_start) + return prev_line == tok_line and token ~= tokens.EOF +end +local error_message = { + function(context, stack, stack_n, regs, token, _startloc_token_, _endloc_token_) + if token == tokens.EQUALS then + -- parse_errors.mlyl, line 13 + return errors.table_key_equals(_startloc_token_, _endloc_token_) + end + end, + function(context, stack, stack_n, regs, token, _startloc_token_, _endloc_token_) + if token == tokens.EQUALS then + -- parse_errors.mlyl, line 17 + return errors.use_double_equals(_startloc_token_, _endloc_token_) + end + end, + function(context, stack, stack_n, regs, token, _startloc_token_, _endloc_token_) + if token == tokens.STRING or token == tokens.NUMBER or token == tokens.TRUE or token == tokens.FALSE or token == tokens.NIL or token == tokens.OSQUARE or token == tokens.OPAREN or token == tokens.IDENT then + local _startloc_last_ = stack[regs[1] + 1] + local _endloc_last_ = stack[regs[2] + 2] + -- parse_errors.mlyl, line 21 + return errors.missing_table_comma(token, _startloc_token_, _endloc_token_, _endloc_last_) + end + end, + function(context, stack, stack_n, regs, token, _startloc_token_, _endloc_token_) + if token == tokens.CPAREN then + local comma = stack[regs[1]] + local _startloc_comma_ = stack[regs[1] + 1] + local _endloc_comma_ = stack[regs[1] + 2] + -- parse_errors.mlyl, line 28 + return errors.trailing_call_comma(_startloc_comma_, _endloc_comma_, _startloc_token_, _endloc_token_) + end + end, + function(context, stack, stack_n, regs, token, _startloc_token_, _endloc_token_) + local open_ = stack[regs[1]] + local _startloc_open__ = stack[regs[1] + 1] + local _endloc_open__ = stack[regs[1] + 2] + -- parse_errors.mlyl, line 34 + return errors.unclosed_brackets(_startloc_open__, _endloc_open__, token, _startloc_token_, _endloc_token_) + end, + function(context, stack, stack_n, regs, token, _startloc_token_, _endloc_token_) + local start = stack[regs[1]] + local _startloc_start_ = stack[regs[1] + 1] + local _endloc_start_ = stack[regs[1] + 2] + -- parse_errors.mlyl, line 37 + return errors.unclosed_label(_startloc_start_, _endloc_start_, token, _startloc_token_, _endloc_token_) + end, + function(context, stack, stack_n, regs, token, _startloc_token_, _endloc_token_) + if token == tokens.DOT then + local local_ = stack[regs[1]] + local _startloc_local__ = stack[regs[1] + 1] + local _endloc_local__ = stack[regs[1] + 2] + -- parse_errors.mlyl, line 41 + return errors.local_function_dot(_startloc_local__, _endloc_local__, _startloc_token_, _endloc_token_) + end + end, + function(context, stack, stack_n, regs, token, _startloc_token_, _endloc_token_) + local _startloc_expr_ = stack[regs[1] + 1] + local _endloc_expr_ = stack[regs[2] + 2] + local if_ = stack[regs[3]] + local _startloc_if__ = stack[regs[3] + 1] + local _endloc_if__ = stack[regs[3] + 2] + -- parse_errors.mlyl, line 45 + local start + if is_same_line(context, _endloc_expr_, token, _startloc_token_) then + start = _startloc_token_ + else + start = _endloc_expr_ + 1 + end + return errors.expected_then(_startloc_if__, _endloc_if__, start) + end, + function(context, stack, stack_n, regs, token, _startloc_token_, _endloc_token_) + local start = stack[regs[1]] + local _startloc_start_ = stack[regs[1] + 1] + local _endloc_start_ = stack[regs[1] + 2] + -- parse_errors.mlyl, line 64 + return errors.expected_end(_startloc_start_, _endloc_start_, token, _startloc_token_, _endloc_token_) + end, + function(context, stack, stack_n, regs, token, _startloc_token_, _endloc_token_) + local func = stack[regs[1]] + local _startloc_func_ = stack[regs[1] + 1] + local _endloc_func_ = stack[regs[1] + 2] + local loc = stack[regs[2]] + local _startloc_loc_ = stack[regs[2] + 1] + local _endloc_loc_ = stack[regs[2] + 2] + -- parse_errors.mlyl, line 68 + return errors.expected_end(_startloc_loc_, _endloc_func_, token, _startloc_token_, _endloc_token_) + end, + function(context, stack, stack_n, regs, token, _startloc_token_, _endloc_token_) + local _startloc_x_ = stack[regs[1] + 1] + local _endloc_x_ = stack[regs[2] + 2] + -- parse_errors.mlyl, line 72 + if not is_same_line(context, _endloc_x_, token, _startloc_token_) then + return errors.standalone_name_call(token, _endloc_x_) + end + end, + function(context, stack, stack_n, regs, token, _startloc_token_, _endloc_token_) + -- parse_errors.mlyl, line 79 + return errors.standalone_name(token, _startloc_token_) + end, + function(context, stack, stack_n, regs, token, _startloc_token_, _endloc_token_) + -- parse_errors.mlyl, line 82 + return errors.standalone_names(token, _startloc_token_) + end, + function(context, stack, stack_n, regs, token, _startloc_token_, _endloc_token_) + -- parse_errors.mlyl, line 86 + if token == tokens.END then + return errors.unexpected_end(_startloc_token_, _endloc_token_) + elseif token ~= tokens.EOF then + return errors.expected_statement(token, _startloc_token_, _endloc_token_) + end + end, + function(context, stack, stack_n, regs, token, _startloc_token_, _endloc_token_) + -- parse_errors.mlyl, line 96 + return errors.expected_function_args(token, _startloc_token_, _endloc_token_) + end, + function(context, stack, stack_n, regs, token, _startloc_token_, _endloc_token_) + -- parse_errors.mlyl, line 100 + return errors.expected_expression(token, _startloc_token_, _endloc_token_) + end, + function(context, stack, stack_n, regs, token, _startloc_token_, _endloc_token_) + -- parse_errors.mlyl, line 104 + return errors.expected_var(token, _startloc_token_, _endloc_token_) + end, +} +local error_message_program_start, error_message_program = 3845, "\7\1\0\8\0\1\2\1\4\19@\0\1\2\8\0\1\2\1\4\17<\0\1\0\8\0\1\2\1\4\19@\0\1\2\8\0\1\2\1\4\18\168\0\1\0\8\0\1\2\1\4\19@\0\8\0\1\2\1\4\18O\0\8\0\1\2\1\4\18\4\0\8\0\1\2\1\4\17\135\0\8\0\1\2\1\4\17\230\0\8\0\1\2\1\4\18\214\0\8\0\1\2\1\4\18\241\0\8\0\1\2\1\4\18\241\0\2\3\1\2\2\0\1\2\8\0\1\2\1\4\18o\0\8\0\1\2\1\4\17\247\0\4\18%\0\4\18%\0\4\17z\0\1\0\4\16\168\0\4\18%\0\1\0\4\16\168\0\4\18%\0\4\18%\0\4\17z\0\1\0\4\16\168\0\4\18%\0\1\0\4\16\168\0\4\17\1\0\4\17\199\0\1\3\1\2\1\1\1\0\4\15:\0\4\17\199\0\1\3\1\2\1\1\1\0\4\15:\0\4\17\199\0\1\0\4\17\19\0\4\17\199\0\1\3\1\2\1\1\1\0\4\15:\0\4\17\199\0\1\3\1\2\1\1\1\0\4\15:\0\4\17\199\0\1\5\1\4\1\3\1\2\1\1\1\0\4\15\n\0\1\5\1\4\1\3\1\2\1\1\1\0\4\15\18\0\4\18C\0\1\0\4\16\239\0\4\16\1\0\4\18I\0\4\16z\0\1\3\1\2\1\1\1\0\4\16r\0\1\3\1\2\1\1\1\0\4\15:\0\1\3\1\2\1\1\1\0\4\15:\0\1\5\1\4\1\3\1\2\1\1\1\0\4\16G\0\4\17\199\0\4\17\199\0\4\17\199\0\4\17\199\0\4\17\199\0\4\17\199\0\4\17\199\0\4\17\199\0\4\17\199\0\4\17\199\0\4\17\199\0\4\17\199\0\4\17\199\0\4\17\199\0\4\17\199\0\4\17\199\0\1\5\1\4\1\3\1\2\1\1\1\0\4\18?\0\1\0\4\17\19\0\1\0\4\18X\0\1\5\1\4\1\3\1\2\1\1\1\0\4\18;\0\4\17\199\0\4\18\0\0\4\18\0\0\4\17\199\0\1\5\1\4\1\3\1\2\1\1\1\0\4\17\178\0\1\5\1\4\1\3\1\2\1\1\1\0\4\17\174\0\4\16%\0\4\17\131\0\4\16\198\0\4\18\223\0\4\0178\0\4\17\1\0\4\17\199\0\4\18I\0\4\17`\0\4\17\199\0\4\18I\0\4\17\199\0\4\17l\0\4\17`\0\4\17\199\0\4\17l\0\1\0\4\17\182\0\4\16\228\0\1\5\1\4\1\3\1\2\1\1\1\0\4\16\150\0\4\18I\0\4\16\224\0\4\16\224\0\4\17\131\0\4\18I\0\4\17\199\0\4\16%\0\4\17\222\0\4\17\199\0\4\16%\0\4\17\226\0\4\17\199\0\4\16%\0\4\17\214\0\4\17X\0\1\0\4\17\182\0\4\17X\0\4\16\198\0\4\17\199\0\4\18I\0\4\17\199\0\4\15B\0\1\0\4\19@\0\1\2\4\17<\0\1\0\4\19@\0\4\15B\0\4\15B\0\1\0\4\19@\0\4\18\206\0\4\19\12\0\4\19\30\0\4\19\30\0\4\19\30\0\4\19\21\0\4\19\3\0\4\19\12\0\4\19\12\0\4\19\3\0\4\19\3\0\4\19\3\0\4\19\3\0\4\19\3\0\4\18\250\0\1\0\4\19@\0\4\19/\0\4\18\4\0\4\17\135\0\4\17\230\0\4\18\214\0\4\17+\0\4\17+\0\2\3\1\2\2\0\1\2\4\18o\0\4\17\247\0\4\15B\0\1\0\4\19@\0\1\2\4\17<\0\1\0\4\19@\0\4\15B\0\4\15B\0\1\0\4\16\143\0\4\16\133\0\1\0\4\19@\0\4\18\206\0\4\19\12\0\4\19\30\0\4\19\30\0\4\19\30\0\4\19\21\0\4\19\3\0\4\19\12\0\4\19\12\0\4\19\3\0\4\19\3\0\4\19\3\0\4\19\3\0\4\19\3\0\4\18\250\0\1\0\4\19@\0\4\19/\0\4\19<\0\4\18\4\0\4\17\135\0\4\17\230\0\4\18\214\0\4\17+\0\4\16\198\0\4\16Y\0\4\17+\0\4\16\164\0\2\3\1\2\2\0\1\2\4\18o\0\4\16\198\0\4\16Y\0\1\0\4\16\232\0\4\17\247\0\4\19Z\0\4\16\133\0\4\15:\0\1\0\4\19@\0\1\2\4\17<\0\1\0\4\19@\0\4\15:\0\4\15:\0\4\15\"\0\1\0\4\19@\0\4\19'\0\4\18\159\0\4\18\177\0\4\18\177\0\4\18\177\0\4\18\168\0\4\18f\0\4\18\159\0\4\18\159\0\4\18f\0\4\18f\0\4\18f\0\4\18f\0\4\18f\0\4\18\197\0\1\0\4\19@\0\4\18O\0\4\0187\0\4\18\4\0\4\17\135\0\4\17\230\0\4\18\214\0\4\18\241\0\4\18\241\0\2\3\1\2\2\0\1\2\4\18o\0\4\15\"\0\4\17\247\0\4\0187\0\4\17\243\0\4\17\243\0\4\17\243\0\1\0\8\0\1\2\1\4\19@\0\1\2\8\0\1\2\1\4\17<\0\1\0\8\0\1\2\1\4\19@\0\1\2\8\0\1\2\1\4\19\21\0\1\0\8\0\1\2\1\4\19@\0\8\0\1\2\1\4\19/\0\8\0\1\2\1\4\18\4\0\8\0\1\2\1\4\17\135\0\8\0\1\2\1\4\17\230\0\8\0\1\2\1\4\18\214\0\8\0\1\2\1\4\17+\0\8\0\1\2\1\4\17+\0\2\3\1\2\2\0\1\2\8\0\1\2\1\4\18o\0\8\0\1\2\1\4\17\247\0\4\15:\0\1\0\4\19@\0\1\2\4\17<\0\1\0\4\19@\0\4\15:\0\4\15:\0\1\0\4\19@\0\4\19'\0\4\18\159\0\4\18\177\0\4\18\177\0\4\18\177\0\4\18\168\0\4\18f\0\4\18\159\0\4\18\159\0\4\18f\0\4\18f\0\4\18f\0\4\18f\0\4\18f\0\4\18\197\0\1\0\4\19@\0\4\18O\0\4\18\4\0\4\17\135\0\4\17\230\0\4\18\214\0\4\18\241\0\4\18\241\0\2\3\1\2\2\0\1\2\4\18o\0\4\17\247\0\4\16\198\0\4\18\142\0\4\18\131\0\4\16\202\0\1\0\4\16~\0\2\5\1\2\4\0\4\16\206\0\4\15B\0\1\0\4\19@\0\1\2\4\17<\0\1\0\4\19@\0\4\15B\0\4\15B\0\1\0\4\19@\0\4\18\206\0\4\19\12\0\4\19\30\0\4\19\30\0\4\19\30\0\4\19\21\0\4\19\3\0\4\19\12\0\4\19\12\0\4\19\3\0\4\19\3\0\4\19\3\0\4\19\3\0\4\19\3\0\4\18\250\0\1\0\4\19@\0\4\19/\0\4\18\4\0\4\17\135\0\4\17\230\0\4\18\214\0\4\17+\0\4\17+\0\2\3\1\2\2\0\1\2\4\18o\0\4\17\247\0\4\19Z\0\2\5\1\2\4\0\4\16\206\0\4\15B\0\1\0\4\19@\0\1\2\4\17<\0\1\0\4\19@\0\4\15B\0\4\15B\0\1\0\4\16\143\0\4\16\133\0\1\0\4\19@\0\4\18\206\0\4\19\12\0\4\19\30\0\4\19\30\0\4\19\30\0\4\19\21\0\4\19\3\0\4\19\12\0\4\19\12\0\4\19\3\0\4\19\3\0\4\19\3\0\4\19\3\0\4\19\3\0\4\18\250\0\1\0\4\19@\0\4\19/\0\4\19<\0\4\18\4\0\4\17\135\0\4\17\230\0\4\18\214\0\4\17+\0\4\16\198\0\4\16Y\0\4\17+\0\4\16\164\0\2\3\1\2\2\0\1\2\4\18o\0\4\16\198\0\4\16Y\0\1\0\4\16\232\0\4\17\247\0\4\19Z\0\4\18\0\0\1\0\4\17\12\0\1\0\8\0\1\2\1\4\19@\0\1\2\8\0\1\2\1\4\17<\0\1\0\8\0\1\2\1\4\19@\0\1\2\8\0\1\2\1\4\19\12\0\1\2\8\0\1\2\1\4\19\21\0\1\2\8\0\1\2\1\4\19\3\0\1\2\8\0\1\2\1\4\19\12\0\1\2\8\0\1\2\1\4\19\12\0\1\2\8\0\1\2\1\4\19\3\0\1\2\8\0\1\2\1\4\19\3\0\1\2\8\0\1\2\1\4\19\3\0\1\2\8\0\1\2\1\4\19\3\0\1\2\8\0\1\2\1\4\19\3\0\1\2\8\0\1\2\1\4\18\250\0\1\0\8\0\1\2\1\4\19@\0\8\0\1\2\1\4\19/\0\8\0\1\2\1\4\18\4\0\8\0\1\2\1\4\17\135\0\8\0\1\2\1\4\17\230\0\8\0\1\2\1\4\18\214\0\8\0\1\2\1\4\17+\0\8\0\1\2\1\4\17+\0\2\3\1\2\2\0\1\2\8\0\1\2\1\4\18o\0\8\0\1\2\1\4\17\247\0\1\0\8\0\1\2\1\4\19@\0\1\2\8\0\1\2\1\4\17<\0\1\0\8\0\1\2\1\4\19@\0\1\2\8\0\1\2\1\4\18\159\0\1\2\8\0\1\2\1\4\18\168\0\1\2\8\0\1\2\1\4\18f\0\1\2\8\0\1\2\1\4\18\159\0\1\2\8\0\1\2\1\4\18\159\0\1\2\8\0\1\2\1\4\18f\0\1\2\8\0\1\2\1\4\18f\0\1\2\8\0\1\2\1\4\18f\0\1\2\8\0\1\2\1\4\18f\0\1\2\8\0\1\2\1\4\18f\0\1\2\8\0\1\2\1\4\18\197\0\1\0\8\0\1\2\1\4\19@\0\8\0\1\2\1\4\18O\0\8\0\1\2\1\4\18\4\0\8\0\1\2\1\4\17\135\0\8\0\1\2\1\4\17\230\0\8\0\1\2\1\4\18\214\0\8\0\1\2\1\4\18\241\0\8\0\1\2\1\4\18\241\0\2\3\1\2\2\0\1\2\8\0\1\2\1\4\18o\0\8\0\1\2\1\4\17\247\0\1\0\8\0\1\2\1\4\19@\0\1\2\8\0\1\2\1\4\17<\0\1\0\8\0\1\2\1\4\19@\0\1\0\8\0\1\2\1\4\19@\0\8\0\1\2\1\4\18O\0\8\0\1\2\1\4\18\4\0\8\0\1\2\1\4\17\135\0\8\0\1\2\1\4\17\230\0\8\0\1\2\1\4\18\214\0\8\0\1\2\1\4\18\241\0\8\0\1\2\1\4\18\241\0\2\3\1\2\2\0\1\2\8\0\1\2\1\4\18o\0\8\0\1\2\1\4\17\247\0\1\0\8\0\1\2\1\4\19@\0\1\2\8\0\1\2\1\4\17<\0\1\0\8\0\1\2\1\4\19@\0\1\0\8\0\1\2\1\4\19@\0\8\0\1\2\1\4\19/\0\8\0\1\2\1\4\18\4\0\8\0\1\2\1\4\17\135\0\8\0\1\2\1\4\17\230\0\8\0\1\2\1\4\18\214\0\8\0\1\2\1\4\17+\0\8\0\1\2\1\4\17+\0\2\3\1\2\2\0\1\2\8\0\1\2\1\4\18o\0\8\0\1\2\1\4\17\247\0\1\0\8\0\1\2\1\4\19@\0\1\2\8\0\1\2\1\4\17<\0\1\0\8\0\1\2\1\4\19@\0\1\2\8\0\1\2\1\4\19\21\0\1\2\8\0\1\2\1\4\19\3\0\1\2\8\0\1\2\1\4\19\12\0\1\2\8\0\1\2\1\4\19\3\0\1\2\8\0\1\2\1\4\19\3\0\1\2\8\0\1\2\1\4\19\3\0\1\2\8\0\1\2\1\4\19\3\0\1\2\8\0\1\2\1\4\19\3\0\1\2\8\0\1\2\1\4\18\250\0\1\0\8\0\1\2\1\4\19@\0\8\0\1\2\1\4\19/\0\8\0\1\2\1\4\18\4\0\8\0\1\2\1\4\17\135\0\8\0\1\2\1\4\17\230\0\8\0\1\2\1\4\18\214\0\8\0\1\2\1\4\17+\0\8\0\1\2\1\4\17+\0\2\3\1\2\2\0\1\2\8\0\1\2\1\4\18o\0\8\0\1\2\1\4\17\247\0\1\0\8\0\1\2\1\4\19@\0\1\2\8\0\1\2\1\4\17<\0\1\0\8\0\1\2\1\4\19@\0\1\2\8\0\1\2\1\4\18\168\0\1\2\8\0\1\2\1\4\18f\0\1\2\8\0\1\2\1\4\18\159\0\1\2\8\0\1\2\1\4\18f\0\1\2\8\0\1\2\1\4\18f\0\1\2\8\0\1\2\1\4\18f\0\1\2\8\0\1\2\1\4\18f\0\1\2\8\0\1\2\1\4\18f\0\1\2\8\0\1\2\1\4\18\197\0\1\0\8\0\1\2\1\4\19@\0\8\0\1\2\1\4\18O\0\8\0\1\2\1\4\18\4\0\8\0\1\2\1\4\17\135\0\8\0\1\2\1\4\17\230\0\8\0\1\2\1\4\18\214\0\8\0\1\2\1\4\18\241\0\8\0\1\2\1\4\18\241\0\2\3\1\2\2\0\1\2\8\0\1\2\1\4\18o\0\8\0\1\2\1\4\17\247\0\1\0\8\0\1\2\1\4\19@\0\1\2\8\0\1\2\1\4\17<\0\1\0\8\0\1\2\1\4\19@\0\1\2\8\0\1\2\1\4\18\168\0\1\2\8\0\1\2\1\4\18\197\0\1\0\8\0\1\2\1\4\19@\0\8\0\1\2\1\4\18O\0\8\0\1\2\1\4\18\4\0\8\0\1\2\1\4\17\135\0\8\0\1\2\1\4\17\230\0\8\0\1\2\1\4\18\214\0\8\0\1\2\1\4\18\241\0\8\0\1\2\1\4\18\241\0\2\3\1\2\2\0\1\2\8\0\1\2\1\4\18o\0\8\0\1\2\1\4\17\247\0\1\0\8\0\1\2\1\4\19@\0\1\2\8\0\1\2\1\4\17<\0\1\0\8\0\1\2\1\4\19@\0\1\2\8\0\1\2\1\4\19\21\0\1\2\8\0\1\2\1\4\18\250\0\1\0\8\0\1\2\1\4\19@\0\8\0\1\2\1\4\19/\0\8\0\1\2\1\4\18\4\0\8\0\1\2\1\4\17\135\0\8\0\1\2\1\4\17\230\0\8\0\1\2\1\4\18\214\0\8\0\1\2\1\4\17+\0\8\0\1\2\1\4\17+\0\2\3\1\2\2\0\1\2\8\0\1\2\1\4\18o\0\8\0\1\2\1\4\17\247\0\1\0\4\16\143\0\4\18\210\0\4\17d\0\4\17T\0\4\17$\0\4\18\186\0\2\5\1\2\4\0\4\18\r\0\4\15B\0\1\0\4\19@\0\1\2\4\17<\0\1\0\4\19@\0\4\15B\0\4\15B\0\1\0\4\19@\0\4\18\206\0\4\19\12\0\4\19\30\0\4\19\30\0\4\19\30\0\4\19\21\0\4\19\3\0\4\19\12\0\4\19\12\0\4\19\3\0\4\19\3\0\4\19\3\0\4\19\3\0\4\19\3\0\4\18\250\0\1\0\4\19@\0\4\19/\0\4\18\4\0\4\17\135\0\4\17\230\0\4\18\214\0\4\17+\0\4\17+\0\2\3\1\2\2\0\1\2\4\18o\0\4\17\247\0\1\0\4\16\143\0\4\18\210\0\4\17X\0\4\0174\0\4\0167\0\4\17h\0\1\0\4\16~\0\4\0167\0\1\0\4\16~\0\2\5\1\2\4\0\4\15\204\0\4\15B\0\1\0\4\19@\0\1\2\4\17<\0\1\0\4\19@\0\4\15B\0\4\15B\0\1\0\4\19@\0\4\18\206\0\4\19\12\0\4\19\30\0\4\19\30\0\4\19\30\0\4\19\21\0\4\19\3\0\4\19\12\0\4\19\12\0\4\19\3\0\4\19\3\0\4\19\3\0\4\19\3\0\4\19\3\0\4\18\250\0\1\0\4\19@\0\4\19/\0\4\18\4\0\4\17\135\0\4\17\230\0\4\18\214\0\4\17+\0\4\17+\0\2\3\1\2\2\0\1\2\4\18o\0\4\17\247\0\6\0\1\0\7\6\0\183\0\4\19T\0\6\1I\0\4\19T\0\6\1\198\0\4\19T\0\6\2&\0\4\19T\0\6\2n\0\4\19T\0\6\2\206\0\4\19T\0\6\3\22\0\4\19T\0\6\3v\0\4\19T\0\6\3\188\0\8\0\1\2\1\4\19T\0\6\4\2\0\8\0\1\2\1\4\19T\0\6\4H\0\8\0\1\2\1\4\19T\0\6\4\142\0\8\0\1\2\1\4\19T\0\6\0\194\0\8\0\1\2\1\4\19T\0\6\4\149\0\8\0\1\2\1\4\19T\0\6\4\219\0\8\0\1\2\1\4\19T\0\6\5\24\0\8\0\1\2\1\4\19T\0\6\5!\0\8\0\1\2\1\4\19T\0\6\5^\0\8\0\1\2\1\4\19T\0\5\0\n\1\2\0\1\6\0\2\0\4\16\24\0\6\0-\0\4\16\24\0\5\0\r\1\0\6\0c\0\7\6\0%\0\7\6\0002\0\7\6\0\6\0\1\0\4\16\168\0\6\0\3\0\1\0\4\16\143\0\6\0\t\0\4\17\170\0\6\0\7\0\7\5\0\r\1\0\6\0\21\0\4\16?\0\5\0\r\1\0\5\0\16\1\0\6\0\25\0\4\16?\0\6\0\27\0\4\16?\0\6\0\17\0\4\17\218\0\6\0\4\0\4\15\"\0\6\0\5\0\1\0\4\16\143\0\5\0\14\1\0\6\0\n\0\7\6\0\11\0\1\2\4\16\180\0\6\0\8\0\7\4\16v\0\4\16\160\0\4\18+\0\5\0\8\1\1\0\7\4\16\137\0\1\0\4\16\143\0\5\0\4\1\1\0\7\2\5\1\2\4\0\4\16\206\0\4\15:\0\4\16\19\0\5\0\8\1\1\0\5\0\r\1\0\7\5\0\2\1\2\0\1\5\0\4\1\1\2\7\4\17\222\0\4\15\219\0\4\17\210\0\5\0\n\1\2\0\1\5\0\11\1\0\5\0\12\1\0\7\4\17\160\0\4\17\214\0\5\0\5\1\1\0\7\5\0\4\1\1\0\5\0\16\1\0\7\5\0\12\1\0\7\5\0\r\1\0\5\0\16\1\0\7\5\0\6\1\1\0\7\5\0\4\1\1\0\5\0\15\1\0\5\0\16\1\0\7\5\0\3\1\1\0\7\5\0\1\1\0\4\17T\0\4\16\198\0\4\0198\0\5\0\0\1\0\5\0\1\1\0\5\0\2\1\2\0\1\5\0\4\1\1\2\7\4\16\198\0\4\0174\0\4\17X\0\4\17\131\0\4\17T\0\4\18}\0\5\0\r\1\0\5\0\16\1\0\4\18}\0\5\0\r\1\0\4\18}\0\4\17\\\0\5\0\0\1\0\5\0\1\1\0\5\0\2\1\2\0\1\4\16O\0\4\18C\0\4\17\156\0\1\0\4\16~\0\4\17\164\0\4\18\21\0\4\0183\0\5\0\8\1\1\0\5\0\r\1\0\5\0\16\1\0\7\5\0\15\1\0\5\0\16\1\0\7\4\17\170\0\4\17`\0\4\16\11\0\4\16\228\0\4\16\194\0\5\0\1\1\0\4\16c\0\4\17\164\0\4\17\239\0\5\0\1\1\0\4\16\198\0\4\16O\0\5\0\1\1\0\4\18!\0\5\0\n\1\2\0\1\7\4\0152\0\4\16c\0\4\19<\0\4\18\25\0\5\0\r\1\0\7\4\15\247\0\4\16G\0\4\15\26\0\4\19+\0\4\18/\0\4\18\29\0\5\0\14\1\0\7\5\0\16\1\0\7\5\0\1\1\0\4\15\242\0\5\0\15\1\0\5\0\16\1\0\4\16m\0\5\0\1\1\0\4\15~\0\5\0\1\1\0\5\0\7\1\3\0\1\2\7\1\0\4\16~\0\5\0\r\1\0\1\0\4\16~\0\5\0\r\1\0\1\0\4\18\153\0\1\1\4\19L\0\5\0\1\1\0\4\15q\0\5\0\1\1\0\4\15\178\0\5\0\1\1\0\4\15W\0\5\0\2\1\2\0\1\4\16O\0\5\0\1\1\0\4\15\152\0\4\15B\0\4\15\227\0\5\0\1\1\0\4\15\227\0\5\0\15\1\0\5\0\16\1\0\4\15\227\0\4\16\251\0\5\0\1\1\0\4\0174\0\5\0\1\1\0\4\15\165\0\5\0\1\1\0\4\15\139\0\5\0\1\1\0\4\15d\0\5\0\1\1\0\4\15\191\0\5\0\1\1\0\4\15J\0\4\15:\0\4\15\"\0\5\0\1\1\0\4\15\237\0\4\18\210\0\4\15*\0\5\0\1\1\0\5\0\4\1\1\0\7\5\0\t\1\2\0\1\7\5\0\1\1\0\7\4\18\237\0" +local error_message_table = "\1\2\0\0\0\1\0\201\1\0\145\0\0\0\0\0\0\5\0\205\6\0\209\7\0\221\8\0\225\t\0\237\n\0\241\11\0\247\12\0\251\r\1\7\14\1\11\15\1\23\16\1\27\17\1+\18\1;\19\1?\20\1E\5\6\5\0\0\0\23\1I\0\0\0\23\4!\0\0\0\27\1M\0\0\0\0\0\0\29\0\145\31\1Q \1]!\1i\0\0\0\0\0\0\0\0\0%\1u&\1\133\0\0\0\0\0\0)\1\137\0\0\0\0\0\0\0\0\0\1\0\173.\1\141\0\0\0000\1\145\0\0\0002\1\149\0\0\0004\1\153\0\0\0006\1\157\0\0\0008\1\161\0\0\0:\1\165\0\0\0<\1\169\0\0\0>\1\173\0\0\0@\1\177\0\0\0B\1\181\0\0\0D\1\185\0\0\0F\1\189\0\0\0H\1\193\29\0\173J\1\197K\1\213\0\0\0M\1\219\0\0\0\0\0\0P\1\225\0\0\0K\r\155" .. ("\0"):rep(15) .. "U\4\195\0\0\0\0\0\0\0\0\0\0\0\0]\1\241\0\0\0\0\0\0`\1\245a\1\249\0\0\0\0\0\0\0\0\0e\1\253b\7s\0\0\0\0\0\0i\2\1\0\0\0k\2\17b\r\159\0\0\0n\2!K\r\137p\2%q\2)r\2-\0\0\0t\0021u\0025u\0\149w\0029\0\0\0y\2=z\2=\0\0\0K\14?|\0\149~\2Ay\5\245{\5\249\129\2Ey\7w\131\2I\0\0\0\133\2M\0\0\0\135\2Q\135\0\153\0\0\0\138\2U\139\2Y\0\0\0\141\2]\141\0\153\143\2a\143\0\157\145\2g\146\2k\0\0\0\148\2{\0\0\0r\r\143\0\0\0\152\2\127\0\0\0\154\2\131\150\5\253\155\0\163\157\2\135\158\2\139\0\0\0\160\2\143u\0\177\162\2\143r\14E\164\2\147\129\r\147\166\2\151\167\2\155|\0\177\169\2\159\162\6\1\171\2\163\0\0\0\173\2\167\0\0\0\175\2\171\0\0\0\177\2\175\129\14I\135\0\181\180\2\179\181\2\183\181\0\167\183\2\189\184\2\193\141\0\181\4\6\171\143\0\185\188\2\197\7\6\181\190\2\201\t\6\185\n\6\191\11\6\197\174\4\207\r\6\203\196\2\205\15\6\207\174\4\203\155\0\191\174\4\199\19\6\211\t\12S\n\12^\11\12i\23\6\217\0\0\0\0\0\0n\14Q" .. ("\0"):rep(33) .. "&\6\221\0\0\0\0\0\0)\6\227\188\r\151\181\0\195\0\0\0\0\0\0.\6\231\0\0\0000\6\235&\12i2\6\239\135\14U4\6\243\0\0\0006\6\247\188\14M8\6\251\141\14U:\6\255\143\14Y<\7\3\0\0\0>\7\7\0\0\0@\7\0116\12tB\7\15\0\0\0D\7\19\0\0\0F\7\23\0\0\0H\7\27\0\0\0\0\0\0K\7\31\0\0\0M\7%\0\0\0\0\0\0\164\14_\0\0\0R\7)H\12\127T\0\0\169\14_K\12\138\0\0\0M\12\149\0\0\0\0\0\0\175\14_\0\0\0]\7-\0\0\0\0\0\0\0\0\0\181\14cb\0071\0\0\0\0\0\0e\0075\0\0\0\0\0\0]\12\158\0\0\0\0\0\0\0\0\0\0\0\0b\12\167\0\0\0\0\0\0e\12\176\0\0\0r\0079\0\0\0\0\0\0\0\0\0\0\0\0w\7=\0\0\0y\7Az\7E\0\0\0\0\0\0r\12\185\0\0\0\0\0\0\0\0\0\129\7Iw\12\194\131\7M\0\0\0\133\7Q" .. ("\0"):rep(15) .. "\139\7Q\129\12\203\0\0\0\0\0\0\0\0\0\133\12\212\0\0\0\146\7]\0\0\0\148\7a\0\0\0\139\12\212\0\0\0\0\0\0\7\3c\0\0\0\t\3g\n\3m\11\3s\158\0\0\r\3y\0\0\0\15\3}\0\0\0\0\0\0\0\0\0\19\3\129\0\0\0\0\0\0\0\0\0\23\3\135" .. ("\0"):rep(24) .. "\178\7e" .. ("\0"):rep(15) .. "&\3\139\0\0\0\0\0\0)\3\145\188\7k\0\0\0\190\7o\0\0\0.\3\149\0\0\0000\3\153\0\0\0002\3\157\0\0\0004\3\161\188\12\2296\3\165\0\0\0008\3\169\0\0\0:\3\173\0\0\0<\3\177\0\0\0>\3\181\0\0\0@\3\185\0\0\0B\3\189\0\0\0D\3\193\0\0\0F\3\197\0\0\0H\3\201\0\0\0\0\0\0K\3\205\0\0\0M\3\211\0\0\0\0\0\0\0\0\0\0\0\0R\3\215\0\0\0T" .. ("\0"):rep(26) .. "]\3\219\0\0\0\0\0\0\0\0\0\0\0\0b\3\223\0\0\0\0\0\0e\3\227" .. ("\0"):rep(36) .. "r\3\231\0\0\0\0\0\0\0\0\0\0\0\0w\3\235\0\0\0y\3\239z\3\243" .. ("\0"):rep(18) .. "\129\3\247\0\0\0\131\3\251\7\4%\133\3\255\t\4)\n\4/\11\0045\0\0\0\r\4;\139\3\255\15\4?" .. ("\0"):rep(15) .. "\146\4\11\0\0\0\148\4\15" .. ("\0"):rep(27) .. "\158\0\0\0\0\0\0\0\0$\4C\0\0\0&\4G\0\0\0\0\0\0)\4M\0\0\0\0\0\0\0\0\0\0\0\0.\4Q\0\0\0000\4U\0\0\0002\4Y\0\0\0004\4]\178\4\0196\4a\0\0\0008\4e\0\0\0:\4i\0\0\0<\4m\0\0\0>\4q\188\4\25@\4u\190\4\29B\4y\0\0\0D\4}\0\0\0F\4\129\0\0\0H\4\133\0\0\0\0\0\0K\4\137\0\0\0M\4\143" .. ("\0"):rep(21) .. "U\4\147" .. ("\0"):rep(21) .. "]\4\151\0\0\0\0\0\0\0\0\0\0\0\0b\4\155\0\0\0\4\14ie\4\159\0\0\0\7\14s\0\0\0\t\14w\n\14}\11\14\131\0\0\0\r\14\137\0\0\0\15\14\141\0\0\0\0\0\0r\4\163\0\0\0\0\0\0\0\0\0\0\0\0w\4\167" .. ("\0"):rep(27) .. "\129\4\171\0\0\0\0\0\0\0\0\0\133\4\175&\14\145\0\0\0\0\0\0)\14\151\0\0\0\139\4\175\0\0\0\0\0\0.\14\155\0\0\0000\14\159\0\0\0002\14\163\0\0\0004\14\167\0\0\0006\14\171\0\0\0008\14\175\0\0\0:\14\179\0\0\0<\14\183\0\0\0>\14\187\0\0\0@\14\191\0\0\0B\14\195\0\0\0D\14\199\0\0\0F\14\203\0\0\0H\14\207\0\0\0\0\0\0K\14\211\4\6\11M\14\217\0\0\0\7\6\21\0\0\0\t\6\25\n\6\31\11\6%\0\0\0\r\6+\0\0\0\15\6/\0\0\0\0\0\0\186\4\187\0\0\0\188\4\191]\14\221\0\0\0\0\0\0\0\0\0\0\0\0b\14\225\0\0\0\0\0\0e\14\229" .. ("\0"):rep(24) .. "&\0063\0\0\0\0\0\0)\0069r\14\233\0\0\0\0\0\0\0\0\0.\6=w\14\2370\6A\0\0\0002\6E\0\0\0004\6I\0\0\0006\6M\0\0\0008\6Q\129\14\241:\6U\0\0\0<\6Y\133\14\245>\6]\0\0\0@\6a\0\0\0B\6e\139\14\245D\6i\0\0\0F\6m\0\0\0H\6q\0\0\0\0\0\0K\6u\0\0\0M\6{" .. ("\0"):rep(45) .. "]\6\127\0\0\0\0\0\0\0\0\0\0\0\0b\6\131\0\0\0\4\r\163e\6\135\0\0\0\7\r\173\0\0\0\t\r\177\n\r\183\11\r\189\0\0\0\r\r\195\0\0\0\15\r\199\0\0\0\0\0\0r\6\139\0\0\0\188\15\1\0\0\0\190\0\0w\6\143" .. ("\0"):rep(27) .. "\129\6\147\0\0\0\0\0\0\0\0\0\133\6\151&\r\203\0\0\0\0\0\0)\r\209\0\0\0\139\6\151\0\0\0\0\0\0.\r\213\0\0\0000\r\217\0\0\0002\r\221\0\0\0004\r\225\0\0\0006\r\229\0\0\0008\r\233\0\0\0:\r\237\0\0\0<\r\241\0\0\0>\r\245\0\0\0@\r\249\0\0\0B\r\253\0\0\0D\14\1\0\0\0F\14\5\0\0\0H\14\t\0\0\0\0\0\0K\14\r\0\0\0M\14\19\0\0\0\7\5c\0\0\0\t\5g\n\5m\11\5s\0\0\0\r\5y\0\0\0\15\5}\0\0\0\0\0\0\0\0\0\0\0\0\188\6\163]\14\23\190\6\167\0\0\0\0\0\0\0\0\0b\14\27\0\0\0\0\0\0e\14\31" .. ("\0"):rep(24) .. "&\5\129\0\0\0\0\0\0)\5\135r\14#\0\0\0\0\0\0\0\0\0.\5\139w\14'0\5\143\0\0\0002\5\147\0\0\0004\5\151\0\0\0006\5\155\0\0\0008\5\159\129\14+:\5\163\0\0\0<\5\167\133\14/>\5\171\0\0\0@\5\175\0\0\0B\5\179\139\14/D\5\183\0\0\0F\5\187\0\0\0H\5\191\0\0\0\0\0\0K\5\195\0\0\0M\5\201" .. ("\0"):rep(45) .. "]\5\205\0\0\0\0\0\0\0\0\0\0\0\0b\5\209\0\0\0\0\0\0e\5\213\0\0\0\7\2\209\0\0\0\t\2\213\n\2\219\11\2\225\0\0\0\r\2\231\0\0\0\15\2\235\0\0\0\0\0\0r\5\217\0\0\0\188\14;\0\0\0\190\0\0w\5\221" .. ("\0"):rep(27) .. "\129\5\225\0\0\0\0\0\0\0\0\0\133\5\229&\2\239\0\0\0\0\0\0)\2\245\0\0\0\139\5\229\0\0\0\0\0\0.\2\249\0\0\0000\2\253\0\0\0002\3\1\0\0\0004\3\5\0\0\0006\3\t\0\0\0008\3\r\0\0\0:\3\17\0\0\0<\3\21\0\0\0>\3\25\0\0\0@\3\29\0\0\0B\3!\0\0\0D\3%\0\0\0F\3)\0\0\0H\3-\0\0\0\0\0\0K\0031\0\0\0M\0037\0\0\0\t\7}\n\7\136\11\7\147" .. ("\0"):rep(30) .. "\188\5\241]\3;\0\0\0\0\0\0\0\0\0\0\0\0b\3?\0\0\0\0\0\0e\3C" .. ("\0"):rep(18) .. "&\7\147" .. ("\0"):rep(15) .. "r\3G\0\0\0.\7\158\0\0\0\0\0\0w\3K\0\0\0\0\0\0\0\0\0\0\0\0006\7\169\0\0\0008\7\180\0\0\0:\7\191\129\3O<\7\202\0\0\0>\7\213\133\3S@\7\224\0\0\0B\7\235\0\0\0D\7\246\139\3SF\8\1\0\0\0H\8\12\0\0\0\0\0\0K\8\23\0\0\0M\8\"\0\0\0\t\8{\n\8\134\11\8\145" .. ("\0"):rep(33) .. "]\8+\0\0\0\0\0\0\0\0\0\0\0\0b\0084\0\0\0\0\0\0e\8=" .. ("\0"):rep(18) .. "&\8\145" .. ("\0"):rep(15) .. "r\8F\0\0\0.\8\156\0\0\0\188\3_w\8O\0\0\0\0\0\0\0\0\0\0\0\0006\8\167\0\0\0008\8\178\0\0\0:\8\189\129\8X<\8\200\0\0\0>\8\211\133\8a@\8\222\0\0\0B\8\233\0\0\0D\8\244\139\8aF\8\255\0\0\0H\t\n\0\0\0\0\0\0K\t\21\0\0\0M\t \0\0\0\t\n\131\n\n\142\11\n\153" .. ("\0"):rep(33) .. "]\t)\0\0\0\0\0\0\0\0\0\0\0\0b\t2\0\0\0\0\0\0e\t;" .. ("\0"):rep(18) .. "&\n\153" .. ("\0"):rep(15) .. "r\tD\0\0\0\0\0\0\0\0\0\188\8rw\tM\0\0\0\0\0\0\0\0\0\0\0\0006\n\164\0\0\0008\n\175\0\0\0:\n\186\129\tV\0\0\0\0\0\0>\n\197\133\t_@\n\208\0\0\0B\n\219\0\0\0D\n\230\139\t_F\n\241\0\0\0H\n\252\0\0\0\0\0\0K\11\7\0\0\0M\11\18\0\0\0\t\11k\n\11v\11\11\129\0\0\0\0\0\0\0\0\0\0\0\0\t\12\238\n\12\249\11\r\4\0\0\0\0\0\0\0\0\0\0\0\0]\11\27\0\0\0\0\0\0\0\0\0\0\0\0b\11$\0\0\0\0\0\0e\11-" .. ("\0"):rep(18) .. "&\11\129" .. ("\0"):rep(15) .. "r\0116&\r\4\0\0\0\0\0\0\188\tpw\11?\0\0\0\0\0\0\0\0\0\0\0\0006\11\140\0\0\0008\11\151\0\0\0:\11\162\129\11H\0\0\0006\r\15>\11\173\133\11Q@\11\184\0\0\0B\11\195\0\0\0D\11\206\139\11QF\11\217\0\0\0H\11\228\0\0\0\0\0\0K\11\239\0\0\0M\11\250\0\0\0H\r\26\0\0\0\0\0\0K\r%\0\0\0M\r0\0\0\0\t\0\1\n\0\12\11\0\23\0\0\0\0\0\0\0\0\0\0\0\0]\12\3\0\0\0\0\0\0\0\0\0\0\0\0b\12\12\0\0\0]\r9e\12\21\0\0\0\0\0\0\0\0\0b\rB\0\0\0\0\0\0e\rK" .. ("\0"):rep(15) .. "r\12\30&\0\23\0\0\0\0\0\0\188\11bw\12'\0\0\0r\rT\0\0\0\0\0\0\0\0\0\0\0\0w\r]\0\0\0\0\0\0\129\0120\0\0\0006\0\"\0\0\0\133\0129\0\0\0\0\0\0\129\rf\0\0\0\0\0\0\139\0129\133\ro" .. ("\0"):rep(15) .. "\139\ro\t\4\211\n\4\222\11\4\233\0\0\0\0\0\0K\0-\0\0\0M\0008\0\0\0\t\ty\n\t\132\11\t\143" .. ("\0"):rep(33) .. "]\0A\0\0\0\0\0\0\0\0\0\0\0\0b\0J&\4\233\0\0\0e\0S" .. ("\0"):rep(18) .. "&\t\143\0\0\0\0\0\0\188\12J\0\0\0\0\0\0r\0\\6\4\244\0\0\0\0\0\0\188\r\128w\0e" .. ("\0"):rep(27) .. "\129\0n\0\0\0\0\0\0\0\0\0\133\0w\0\0\0\0\0\0K\4\255\0\0\0M\5\n\139\0w\t\t\254\n\n\t\11\n\20\0\0\0\0\0\0K\t\154\0\0\0M\t\165" .. ("\0"):rep(18) .. "]\5\19\0\0\0\0\0\0\0\0\0\0\0\0b\5\28\0\0\0\0\0\0e\5%]\t\174\0\0\0\0\0\0\0\0\0\0\0\0b\t\183&\n\20\0\0\0e\t\192\0\0\0\0\0\0\0\0\0r\5.\0\0\0\0\0\0\0\0\0\0\0\0w\0057\0\0\0\0\0\0\0\0\0r\t\201\0\0\0\0\0\0\0\0\0\188\0\136w\t\210\129\5@\0\0\0\0\0\0\0\0\0\133\5I\0\0\0\0\0\0\0\0\0\0\0\0\129\t\219\139\5I\0\0\0\0\0\0\133\t\228\0\0\0\0\0\0K\n\31\0\0\0M\n*\139\t\228" .. ("\0"):rep(42) .. "]\n3\0\0\0\0\0\0\0\0\0\0\0\0b\n<\0\0\0\0\0\0e\nE" .. ("\0"):rep(36) .. "r\nN\0\0\0\0\0\0\0\0\0\188\5Zw\nW" .. ("\0"):rep(21) .. "\188\t\245\0\0\0\129\n`\0\0\0\0\0\0\0\0\0\133\ni" .. ("\0"):rep(15) .. "\139\ni" .. ("\0"):rep(144) .. "\188\nz" + +local function handle_error(context, stack, stack_n, token, token_start, token_end) + -- Run our error handling virtual machine. + local pc, top, registers, messages = error_message_program_start, stack_n, {}, {} + while true do + local instruction = error_message_program:byte(pc + 1) + if instruction == 1 then -- Store(reg:8b) + local reg = error_message_program:byte(pc + 2) + registers[reg + 1] = top + pc = pc + 2 + elseif instruction == 2 then -- Move(r1:8b, r2:8b) + local r1 = error_message_program:byte(pc + 2) + local r2 = error_message_program:byte(pc + 3) + registers[r2 + 1] = registers[r1 + 1] + pc = pc + 3 + elseif instruction == 3 then -- Clear(reg:8b) + local reg = error_message_program:byte(pc + 2) + registers[reg + 1] = nil + pc = pc + 2 + elseif instruction == 4 then -- Yield (Pop one item from the stack and jump) + -- TODO: Add support for interpret_last. Do we need this? + if top > 1 then top = top - 3 end + pc = get_int(error_message_program, pc + 1, 3) + elseif instruction == 5 then -- Accept + local clause = get_int(error_message_program, pc + 1, 2) + local priority, arity = error_message_program:byte(pc + 4, pc + 5) + local accept = { clause = clause + 1, priority = priority } + for i = 1, arity do accept[i] = registers[error_message_program:byte(pc + 5 + i) + 1] end + messages[#messages + 1] = accept + + pc = pc + 5 + arity + elseif instruction == 6 then -- Match(index:24b) + local index = get_int(error_message_program, pc + 1, 3) + local lr1 = stack[top] - 1 + + local ksize, vsize = error_message_table:byte(1, 2) + local offset = 2 + (index + lr1) * (ksize + vsize) + if offset + 4 + ksize <= #error_message_table and + get_int(error_message_table, offset, ksize) == lr1 + 1 then + pc = get_int(error_message_table, offset + ksize, vsize) + else + pc = pc + 4 + end + elseif instruction == 7 then -- Halt + break + elseif instruction == 8 then -- Priority(clause:16b, p1:8b, p2:8b) + local clause = get_int(error_message_program, pc + 1, 2) + local p1, p2 = error_message_program:byte(pc + 3, pc + 5) + + for i = 1, #messages do + local msg = messages[i] + if msg.clause == clause and msg.priority == p1 then msg.priority = p2 end + end + pc = pc + 5 + else + error("Illegal instruction while handling errors " .. tostring(instruction)) + end + end + + -- Sort the list to ensure earlier patterns are used first. + table.sort(messages, function(a, b) + if a.clause == b.clause then + return a.priority < b.priority + else + return a.clause < b.clause + end + end) + + -- Then loop until we find an error message which actually works! + for i = 1, #messages do + local action = messages[i] + local message = error_message[action.clause](context, stack, stack_n, action, token, token_start, token_end) + if message then + context.report(message) + return false + end + end + + context.report(errors.unexpected_token, token, token_start, token_end) + return false +end + +--- The list of productions in our grammar. Each is a tuple of `terminal * production size`. +local productions = { + { 55, 1 }, { 54, 1 }, { 84, 1 }, { 84, 1 }, { 83, 3 }, { 82, 1 }, + { 82, 1 }, { 82, 1 }, { 82, 1 }, { 82, 1 }, { 82, 1 }, { 82, 1 }, + { 82, 1 }, { 82, 4 }, { 81, 2 }, { 81, 4 }, { 80, 3 }, { 80, 1 }, + { 80, 1 }, { 79, 1 }, { 79, 3 }, { 79, 3 }, { 79, 3 }, { 79, 3 }, + { 79, 3 }, { 79, 3 }, { 79, 3 }, { 79, 3 }, { 79, 3 }, { 79, 3 }, + { 79, 3 }, { 79, 3 }, { 79, 3 }, { 79, 3 }, { 78, 1 }, { 78, 3 }, + { 78, 2 }, { 78, 2 }, { 78, 2 }, { 77, 1 }, { 77, 3 }, { 77, 3 }, + { 76, 1 }, { 76, 1 }, { 75, 0 }, { 75, 2 }, { 75, 3 }, { 74, 1 }, + { 74, 3 }, { 74, 4 }, { 73, 2 }, { 72, 2 }, { 71, 0 }, { 71, 5 }, + { 70, 0 }, { 70, 2 }, { 69, 0 }, { 69, 1 }, { 68, 0 }, { 68, 1 }, + { 67, 1 }, { 67, 3 }, { 66, 1 }, { 66, 3 }, { 65, 1 }, { 65, 3 }, + { 64, 1 }, { 64, 3 }, { 63, 1 }, { 63, 3 }, { 63, 1 }, { 62, 3 }, + { 62, 3 }, { 62, 5 }, { 62, 4 }, { 62, 6 }, { 62, 8 }, { 62, 9 }, + { 62, 11 }, { 62, 7 }, { 62, 2 }, { 62, 4 }, { 62, 6 }, { 62, 5 }, + { 62, 1 }, { 62, 1 }, { 62, 1 }, { 62, 2 }, { 62, 3 }, { 61, 2 }, + { 60, 3 }, { 59, 0 }, { 59, 1 }, { 59, 3 }, { 58, 1 }, { 58, 3 }, + { 58, 5 }, { 57, 1 }, { 57, 1 }, { 56, 1 }, +} + +local f = false + +--[[- The state machine used for our grammar. + +Most LR(1) parsers will encode the transition table in a compact binary format, +optimised for space and fast lookups. However, without access to built-in +bitwise operations, this is harder to justify in Lua. Instead, the transition +table is a 2D lookup table of `action = transitions[state][value]`, where +`action` can be one of the following: + + - `action = false`: This transition is undefined, and thus a parse error. We + use this (rather than nil) to ensure our tables are dense, and thus stored as + arrays rather than maps. + + - `action > 0`: Shift this terminal or non-terminal onto the stack, then + transition to `state = action`. + + - `action < 0`: Apply production `productions[-action]`. This production is a + tuple composed of the next state and the number of values to pop from the + stack. +]] +local transitions = { + { -55, f, f, f, f, f, -55, -55, -55, f, f, f, -55, f, f, f, f, f, f, f, f, -55, f, f, f, -55, -55, f, -55, f, -55, -55, f, f, f, -55, f, f, f, -55, f, f, -55, f, f, f, f, f, f, f, -55, f, f, f, f, f, f, f, f, f, 2, f, f, f, f, f, f, f, f, 4, f, f, 195 }, + { f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 3 }, + { f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, -51 }, + { 5, -45, f, f, f, f, 113, 114, 117, f, f, f, 9, f, f, f, f, f, f, f, f, 121, f, f, f, 133, 16, f, 146, f, 148, 158, f, f, f, -45, -45, -45, -45, 178, f, f, 181, f, f, f, f, f, f, f, 184, f, f, f, f, 34, f, f, f, f, f, 185, 186, f, 187, f, f, f, f, f, f, f, f, 192, 193, 24, f, f, f, f, 194 }, + { f, f, 6, f, 7, 8, f, f, f, f, f, f, 9, 10, 12, 13, 14, f, f, f, f, f, 15, f, f, f, 16, f, 17, f, 18, f, 32, f, f, f, f, f, f, f, 33, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 34, f, f, f, 35, f, f, 36, f, f, f, f, f, f, f, f, f, f, 39, f, 24, f, 40, 109, f, 43, 44 }, + { -9, -9, f, -9, -9, f, -9, -9, -9, -9, f, -9, -9, f, f, f, f, -9, -9, -9, -9, -9, f, -9, f, -9, -9, -9, -9, -9, -9, -9, f, f, -9, -9, -9, -9, -9, -9, f, f, -9, -9, -9, -9, -9, -9, f, -9, -9, -9, -9 }, + { f, f, 6, f, 7, 8, f, f, f, f, f, f, 9, 10, 12, 13, 14, f, f, f, f, f, 15, f, f, f, 16, f, 17, f, 18, f, 32, f, f, f, f, f, f, f, 33, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 34, f, f, f, 35, f, f, 36, f, f, f, f, f, f, f, f, f, f, 39, f, 24, f, 108, f, f, 43, 44 }, + { -13, -13, f, -13, -13, f, -13, -13, -13, -13, f, -13, -13, f, f, f, f, -13, -13, -13, -13, -13, f, -13, f, -13, -13, -13, -13, -13, -13, -13, f, f, -13, -13, -13, -13, -13, -13, f, f, -13, -13, -13, -13, -13, -13, f, -13, -13, -13, -13 }, + { f, f, 6, f, 7, 8, f, f, f, f, f, f, 9, 10, 12, 13, 14, f, f, f, f, f, 15, f, f, f, 16, f, 17, f, 18, f, 32, f, f, f, f, f, f, f, 33, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 34, f, f, f, 35, f, f, 36, f, f, f, f, f, f, f, f, f, f, 39, f, 24, f, 40, 106, f, 43, 44 }, + { f, f, 6, f, 7, 8, f, f, f, f, 11, f, 9, 10, 12, 13, 14, f, f, f, f, f, 15, f, f, f, 16, f, 17, f, 18, f, 32, f, f, f, f, f, f, f, 33, f, f, f, f, f, f, f, f, -92, f, f, f, f, f, 34, f, 95, 104, 35, f, f, 36, f, f, f, f, f, f, f, f, f, f, 39, f, 100, f, 40, 103, f, 43, 44 }, + { f, f, 6, f, 7, 8, f, f, f, f, f, f, 9, 10, 12, 13, 14, f, f, f, f, f, 15, f, f, f, 16, f, 17, f, 18, f, 32, f, f, f, f, f, f, f, 33, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 34, f, f, f, 35, f, f, 36, f, f, f, f, f, f, f, f, f, f, 39, f, 24, f, 40, 91, f, 43, 44 }, + { -12, -12, f, -12, -12, f, -12, -12, -12, -12, f, -12, -12, f, f, f, f, -12, -12, -12, -12, -12, f, -12, f, -12, -12, -12, -12, -12, -12, -12, f, f, -12, -12, -12, -12, -12, -12, f, f, -12, -12, -12, -12, -12, -12, f, -12, -12, -12, -12 }, + { f, f, 6, f, 7, 8, f, f, f, f, f, f, 9, 10, 12, 13, 14, f, f, f, f, f, 15, f, f, f, 16, f, 17, f, 18, f, 32, f, f, f, f, f, f, f, 33, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 34, f, f, f, 35, f, f, 36, f, f, f, f, f, f, f, f, f, f, 39, f, 24, f, 90, f, f, 43, 44 }, + { -8, -8, f, -8, -8, f, -8, -8, -8, -8, f, -8, -8, f, f, f, f, -8, -8, -8, -8, -8, f, -8, f, -8, -8, -8, -8, -8, -8, -8, f, f, -8, -8, -8, -8, -8, -8, f, f, -8, -8, -8, -8, -8, -8, f, -8, -8, -8, -8 }, + { f, f, 6, f, 7, 8, f, f, f, f, f, f, 9, 10, 12, 13, 14, f, f, f, f, f, 15, f, f, f, 16, f, 17, f, 18, f, 32, f, f, f, f, f, f, f, 33, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 34, f, f, f, 35, f, f, 36, f, f, f, f, f, f, f, f, f, f, 39, f, 24, f, 89, f, f, 43, 44 }, + { -43, -43, f, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, f, f, f, -43, -43, -43, -43, -43, f, -43, -43, -43, -43, -43, -43, -43, -43, -43, f, -43, -43, -43, -43, -43, -43, -43, f, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43 }, + { -44, -44, f, -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, f, f, f, -44, -44, -44, -44, -44, f, -44, -44, -44, -44, -44, -44, -44, -44, -44, f, -44, -44, -44, -44, -44, -44, -44, f, -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, -44 }, + { f, f, f, f, f, f, f, f, f, f, f, f, 19, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 29 }, + { f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 16, f, 17, f, f, f, f, f, f, f, f, f, f, f, 20, f, f, f, f, -57, f, f, f, f, f, f, f, f, f, 21, f, f, f, f, f, f, f, f, f, f, 22, f, 26, f, f, f, f, f, f, 24, f, f, f, f, f, f, f, 28 }, + { f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, -4, f, -4 }, + { f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, -3, f, -3 }, + { f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, -58, f, 23 }, + { f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 16, f, 17, f, f, f, f, f, f, f, f, f, f, f, 20, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 21, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 24, f, f, f, f, f, f, f, 25 }, + { -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100 }, + { f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, -62, f, -62 }, + { f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 27 }, + { -5, f, f, f, f, f, -5, -5, -5, f, f, f, -5, f, f, f, f, f, f, f, f, -5, f, f, f, -5, -5, f, -5, f, -5, -5, f, f, f, f, -5, f, f, -5, f, f, -5, f, f, f, f, f, f, f, -5 }, + { f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, -61, f, -61 }, + { -55, f, f, f, f, f, -55, -55, -55, f, f, f, -55, f, f, f, f, f, f, f, f, -55, f, f, f, -55, -55, f, -55, f, -55, -55, f, f, f, f, -55, f, f, -55, f, f, -55, f, f, f, f, f, f, f, -55, f, f, f, f, f, f, f, f, f, 30, f, f, f, f, f, f, f, f, 4 }, + { f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 31 }, + { -14, -14, f, -14, -14, f, -14, -14, -14, -14, f, -14, -14, f, f, f, f, -14, -14, -14, -14, -14, f, -14, f, -14, -14, -14, -14, -14, -14, -14, f, f, -14, -14, -14, -14, -14, -14, f, f, -14, -14, -14, -14, -14, -14, f, -14, -14, -14, -14 }, + { -10, -10, f, -10, -10, f, -10, -10, -10, -10, f, -10, -10, f, f, f, f, -10, -10, -10, -10, -10, f, -10, f, -10, -10, -10, -10, -10, -10, -10, f, f, -10, -10, -10, -10, -10, -10, f, f, -10, -10, -10, -10, -10, -10, f, -10, -10, -10, -10 }, + { -11, -11, f, -11, -11, f, -11, -11, -11, -11, f, -11, -11, f, f, f, f, -11, -11, -11, -11, -11, f, -11, f, -11, -11, -11, -11, -11, -11, -11, f, f, -11, -11, -11, -11, -11, -11, f, f, -11, -11, -11, -11, -11, -11, f, -11, -11, -11, -11 }, + { -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48 }, + { -7, -7, f, -7, -7, f, -7, -7, -7, -7, f, -7, -7, f, f, f, f, -7, -7, -7, -7, -7, f, -7, f, -7, -7, -7, -7, -7, -7, -7, f, f, -7, -7, -7, -7, -7, -7, f, f, -7, -7, -7, -7, -7, -7, f, -7, -7, -7, -7 }, + { -6, -6, f, -6, -6, 37, -6, -6, -6, -6, 38, -6, 75, 10, f, f, f, -6, -6, -6, -6, -6, f, -6, f, -6, -6, -6, -6, -6, -6, -6, f, f, -6, -6, -6, -6, -6, -6, f, 82, -6, -6, -6, -6, -6, -6, 84, -6, -6, -6, -6, f, f, f, f, f, f, 86, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 88 }, + { -18, -18, f, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, f, f, f, -18, -18, -18, -18, -18, f, -18, f, -18, -18, -18, -18, -18, -18, -18, f, f, -18, -18, -18, -18, -18, -18, f, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18 }, + { f, f, 6, f, 7, 8, f, f, f, f, f, f, 9, 10, 12, 13, 14, f, f, f, f, f, 15, f, f, f, 16, f, 17, f, 18, f, 32, f, f, f, f, f, f, f, 33, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 34, f, f, f, 35, f, f, 36, f, f, f, f, f, f, f, f, f, f, 39, f, 24, f, 40, 45, f, 43, 44 }, + { -69, -69, f, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, f, f, f, -69, -69, -69, -69, -69, f, -69, f, -69, -69, -69, -69, -69, -69, -69, f, f, -69, -69, -69, -69, -69, -69, f, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69 }, + { -20, -20, f, -20, -20, f, -20, -20, -20, 41, f, -20, -20, f, f, f, f, -20, -20, -20, -20, -20, f, -20, f, -20, -20, -20, -20, -20, -20, -20, f, f, -20, -20, -20, -20, -20, -20, f, f, -20, -20, -20, -20, -20, -20, f, -20, -20, -20, -20 }, + { f, f, 6, f, 7, 8, f, f, f, f, f, f, 9, 10, 12, 13, 14, f, f, f, f, f, 15, f, f, f, 16, f, 17, f, 18, f, 32, f, f, f, f, f, f, f, 33, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 34, f, f, f, 35, f, f, 36, f, f, f, f, f, f, f, f, f, f, 39, f, 24, f, 42, f, f, 43, 44 }, + { -36, -36, -36, -36, -36, -36, -36, -36, -36, 41, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36 }, + { -71, -71, f, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, f, f, f, -71, -71, -71, -71, -71, f, -71, f, -71, -71, -71, -71, -71, -71, -71, f, f, -71, -71, -71, -71, -71, -71, f, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71 }, + { -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35 }, + { f, f, f, f, 46, f, f, f, f, f, f, 54, f, f, f, f, f, 56, 48, 50, 62, f, f, 64, f, f, f, 66, f, 68, f, f, f, f, 70, f, f, f, f, f, f, f, f, 52, 74, f, 58, f, f, f, f, 72, 60 }, + { f, f, 6, f, 7, 8, f, f, f, f, f, f, 9, 10, 12, 13, 14, f, f, f, f, f, 15, f, f, f, 16, f, 17, f, 18, f, 32, f, f, f, f, f, f, f, 33, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 34, f, f, f, 35, f, f, 36, f, f, f, f, f, f, f, f, f, f, 39, f, 24, f, 40, 47, f, 43, 44 }, + { -24, -24, f, -24, -24, f, -24, -24, -24, f, f, -24, -24, f, f, f, f, -24, 48, 50, -24, -24, f, -24, f, -24, -24, -24, -24, -24, -24, -24, f, f, -24, -24, -24, -24, -24, -24, f, f, -24, 52, -24, -24, -24, -24, f, -24, -24, -24, -24 }, + { f, f, 6, f, 7, 8, f, f, f, f, f, f, 9, 10, 12, 13, 14, f, f, f, f, f, 15, f, f, f, 16, f, 17, f, 18, f, 32, f, f, f, f, f, f, f, 33, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 34, f, f, f, 35, f, f, 36, f, f, f, f, f, f, f, f, f, f, 39, f, 24, f, 40, 49, f, 43, 44 }, + { -25, -25, f, -25, -25, f, -25, -25, -25, f, f, -25, -25, f, f, f, f, -25, -25, -25, -25, -25, f, -25, f, -25, -25, -25, -25, -25, -25, -25, f, f, -25, -25, -25, -25, -25, -25, f, f, -25, -25, -25, -25, -25, -25, f, -25, -25, -25, -25 }, + { f, f, 6, f, 7, 8, f, f, f, f, f, f, 9, 10, 12, 13, 14, f, f, f, f, f, 15, f, f, f, 16, f, 17, f, 18, f, 32, f, f, f, f, f, f, f, 33, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 34, f, f, f, 35, f, f, 36, f, f, f, f, f, f, f, f, f, f, 39, f, 24, f, 40, 51, f, 43, 44 }, + { -27, -27, f, -27, -27, f, -27, -27, -27, f, f, -27, -27, f, f, f, f, -27, -27, -27, -27, -27, f, -27, f, -27, -27, -27, -27, -27, -27, -27, f, f, -27, -27, -27, -27, -27, -27, f, f, -27, -27, -27, -27, -27, -27, f, -27, -27, -27, -27 }, + { f, f, 6, f, 7, 8, f, f, f, f, f, f, 9, 10, 12, 13, 14, f, f, f, f, f, 15, f, f, f, 16, f, 17, f, 18, f, 32, f, f, f, f, f, f, f, 33, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 34, f, f, f, 35, f, f, 36, f, f, f, f, f, f, f, f, f, f, 39, f, 24, f, 40, 53, f, 43, 44 }, + { -26, -26, f, -26, -26, f, -26, -26, -26, f, f, -26, -26, f, f, f, f, -26, -26, -26, -26, -26, f, -26, f, -26, -26, -26, -26, -26, -26, -26, f, f, -26, -26, -26, -26, -26, -26, f, f, -26, -26, -26, -26, -26, -26, f, -26, -26, -26, -26 }, + { f, f, 6, f, 7, 8, f, f, f, f, f, f, 9, 10, 12, 13, 14, f, f, f, f, f, 15, f, f, f, 16, f, 17, f, 18, f, 32, f, f, f, f, f, f, f, 33, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 34, f, f, f, 35, f, f, 36, f, f, f, f, f, f, f, f, f, f, 39, f, 24, f, 40, 55, f, 43, 44 }, + { -22, -22, f, -22, 46, f, -22, -22, -22, f, f, -22, -22, f, f, f, f, 56, 48, 50, 62, -22, f, 64, f, -22, -22, 66, -22, 68, -22, -22, f, f, 70, -22, -22, -22, -22, -22, f, f, -22, 52, -22, -22, 58, -22, f, -22, -22, 72, 60 }, + { f, f, 6, f, 7, 8, f, f, f, f, f, f, 9, 10, 12, 13, 14, f, f, f, f, f, 15, f, f, f, 16, f, 17, f, 18, f, 32, f, f, f, f, f, f, f, 33, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 34, f, f, f, 35, f, f, 36, f, f, f, f, f, f, f, f, f, f, 39, f, 24, f, 40, 57, f, 43, 44 }, + { -30, -30, f, -30, 46, f, -30, -30, -30, f, f, -30, -30, f, f, f, f, -30, 48, 50, -30, -30, f, -30, f, -30, -30, -30, -30, -30, -30, -30, f, f, -30, -30, -30, -30, -30, -30, f, f, -30, 52, -30, -30, 58, -30, f, -30, -30, -30, 60 }, + { f, f, 6, f, 7, 8, f, f, f, f, f, f, 9, 10, 12, 13, 14, f, f, f, f, f, 15, f, f, f, 16, f, 17, f, 18, f, 32, f, f, f, f, f, f, f, 33, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 34, f, f, f, 35, f, f, 36, f, f, f, f, f, f, f, f, f, f, 39, f, 24, f, 40, 59, f, 43, 44 }, + { -28, -28, f, -28, 46, f, -28, -28, -28, f, f, -28, -28, f, f, f, f, -28, 48, 50, -28, -28, f, -28, f, -28, -28, -28, -28, -28, -28, -28, f, f, -28, -28, -28, -28, -28, -28, f, f, -28, 52, -28, -28, 58, -28, f, -28, -28, -28, 60 }, + { f, f, 6, f, 7, 8, f, f, f, f, f, f, 9, 10, 12, 13, 14, f, f, f, f, f, 15, f, f, f, 16, f, 17, f, 18, f, 32, f, f, f, f, f, f, f, 33, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 34, f, f, f, 35, f, f, 36, f, f, f, f, f, f, f, f, f, f, 39, f, 24, f, 40, 61, f, 43, 44 }, + { -23, -23, f, -23, -23, f, -23, -23, -23, f, f, -23, -23, f, f, f, f, -23, 48, 50, -23, -23, f, -23, f, -23, -23, -23, -23, -23, -23, -23, f, f, -23, -23, -23, -23, -23, -23, f, f, -23, 52, -23, -23, -23, -23, f, -23, -23, -23, -23 }, + { f, f, 6, f, 7, 8, f, f, f, f, f, f, 9, 10, 12, 13, 14, f, f, f, f, f, 15, f, f, f, 16, f, 17, f, 18, f, 32, f, f, f, f, f, f, f, 33, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 34, f, f, f, 35, f, f, 36, f, f, f, f, f, f, f, f, f, f, 39, f, 24, f, 40, 63, f, 43, 44 }, + { -31, -31, f, -31, 46, f, -31, -31, -31, f, f, -31, -31, f, f, f, f, -31, 48, 50, -31, -31, f, -31, f, -31, -31, -31, -31, -31, -31, -31, f, f, -31, -31, -31, -31, -31, -31, f, f, -31, 52, -31, -31, 58, -31, f, -31, -31, -31, 60 }, + { f, f, 6, f, 7, 8, f, f, f, f, f, f, 9, 10, 12, 13, 14, f, f, f, f, f, 15, f, f, f, 16, f, 17, f, 18, f, 32, f, f, f, f, f, f, f, 33, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 34, f, f, f, 35, f, f, 36, f, f, f, f, f, f, f, f, f, f, 39, f, 24, f, 40, 65, f, 43, 44 }, + { -32, -32, f, -32, 46, f, -32, -32, -32, f, f, -32, -32, f, f, f, f, -32, 48, 50, -32, -32, f, -32, f, -32, -32, -32, -32, -32, -32, -32, f, f, -32, -32, -32, -32, -32, -32, f, f, -32, 52, -32, -32, 58, -32, f, -32, -32, -32, 60 }, + { f, f, 6, f, 7, 8, f, f, f, f, f, f, 9, 10, 12, 13, 14, f, f, f, f, f, 15, f, f, f, 16, f, 17, f, 18, f, 32, f, f, f, f, f, f, f, 33, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 34, f, f, f, 35, f, f, 36, f, f, f, f, f, f, f, f, f, f, 39, f, 24, f, 40, 67, f, 43, 44 }, + { -33, -33, f, -33, 46, f, -33, -33, -33, f, f, -33, -33, f, f, f, f, -33, 48, 50, -33, -33, f, -33, f, -33, -33, -33, -33, -33, -33, -33, f, f, -33, -33, -33, -33, -33, -33, f, f, -33, 52, -33, -33, 58, -33, f, -33, -33, -33, 60 }, + { f, f, 6, f, 7, 8, f, f, f, f, f, f, 9, 10, 12, 13, 14, f, f, f, f, f, 15, f, f, f, 16, f, 17, f, 18, f, 32, f, f, f, f, f, f, f, 33, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 34, f, f, f, 35, f, f, 36, f, f, f, f, f, f, f, f, f, f, 39, f, 24, f, 40, 69, f, 43, 44 }, + { -34, -34, f, -34, 46, f, -34, -34, -34, f, f, -34, -34, f, f, f, f, -34, 48, 50, -34, -34, f, -34, f, -34, -34, -34, -34, -34, -34, -34, f, f, -34, -34, -34, -34, -34, -34, f, f, -34, 52, -34, -34, 58, -34, f, -34, -34, -34, 60 }, + { f, f, 6, f, 7, 8, f, f, f, f, f, f, 9, 10, 12, 13, 14, f, f, f, f, f, 15, f, f, f, 16, f, 17, f, 18, f, 32, f, f, f, f, f, f, f, 33, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 34, f, f, f, 35, f, f, 36, f, f, f, f, f, f, f, f, f, f, 39, f, 24, f, 40, 71, f, 43, 44 }, + { -29, -29, f, -29, 46, f, -29, -29, -29, f, f, -29, -29, f, f, f, f, -29, 48, 50, -29, -29, f, -29, f, -29, -29, -29, -29, -29, -29, -29, f, f, -29, -29, -29, -29, -29, -29, f, f, -29, 52, -29, -29, 58, -29, f, -29, -29, -29, 60 }, + { f, f, 6, f, 7, 8, f, f, f, f, f, f, 9, 10, 12, 13, 14, f, f, f, f, f, 15, f, f, f, 16, f, 17, f, 18, f, 32, f, f, f, f, f, f, f, 33, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 34, f, f, f, 35, f, f, 36, f, f, f, f, f, f, f, f, f, f, 39, f, 24, f, 40, 73, f, 43, 44 }, + { -21, -21, f, -21, 46, f, -21, -21, -21, f, f, -21, -21, f, f, f, f, 56, 48, 50, 62, -21, f, 64, f, -21, -21, 66, -21, 68, -21, -21, f, f, 70, -21, -21, -21, -21, -21, f, f, -21, 52, -21, -21, 58, -21, f, -21, -21, -21, 60 }, + { -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50 }, + { f, f, 6, f, 7, 8, f, f, f, f, f, f, 9, 10, 12, 13, 14, f, f, f, f, f, 15, f, f, f, 16, f, 17, f, 18, f, 32, f, f, f, f, f, f, f, 33, f, f, f, f, -59, f, f, f, f, f, f, f, f, f, 34, f, f, f, 35, f, f, 36, f, f, 76, f, 79, f, f, f, f, f, 39, f, 24, f, 40, 81, f, 43, 44 }, + { f, -60, f, f, f, f, -60, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, -60, -60, -60, -60, f, f, f, f, f, f, -60, f, 77 }, + { f, f, 6, f, 7, 8, f, f, f, f, f, f, 9, 10, 12, 13, 14, f, f, f, f, f, 15, f, f, f, 16, f, 17, f, 18, f, 32, f, f, f, f, f, f, f, 33, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 34, f, f, f, 35, f, f, 36, f, f, f, f, f, f, f, f, f, f, 39, f, 24, f, 40, 78, f, 43, 44 }, + { -64, -64, f, f, 46, f, -64, -64, -64, f, f, 54, -64, f, f, f, f, 56, 48, 50, 62, -64, f, 64, f, -64, -64, 66, -64, 68, -64, -64, f, f, 70, -64, -64, -64, -64, -64, f, f, -64, 52, f, -64, 58, -64, f, f, -64, 72, 60 }, + { f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 80 }, + { -17, -17, f, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, f, f, f, -17, -17, -17, -17, -17, f, -17, f, -17, -17, -17, -17, -17, -17, -17, f, f, -17, -17, -17, -17, -17, -17, f, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17 }, + { -63, -63, f, f, 46, f, -63, -63, -63, f, f, 54, -63, f, f, f, f, 56, 48, 50, 62, -63, f, 64, f, -63, -63, 66, -63, 68, -63, -63, f, f, 70, -63, -63, -63, -63, -63, f, f, -63, 52, f, -63, 58, -63, f, f, -63, 72, 60 }, + { f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 16, f, 17, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 83 }, + { -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49 }, + { f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 16, f, 17, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 85 }, + { f, f, f, f, f, 37, f, f, f, f, f, f, 75, 10, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 86, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 87 }, + { -19, -19, f, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, f, f, f, -19, -19, -19, -19, -19, f, -19, f, -19, -19, -19, -19, -19, -19, -19, f, f, -19, -19, -19, -19, -19, -19, f, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19 }, + { -16, -16, f, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, f, f, f, -16, -16, -16, -16, -16, f, -16, f, -16, -16, -16, -16, -16, -16, -16, f, f, -16, -16, -16, -16, -16, -16, f, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16 }, + { -15, -15, f, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, f, f, f, -15, -15, -15, -15, -15, f, -15, f, -15, -15, -15, -15, -15, -15, -15, f, f, -15, -15, -15, -15, -15, -15, f, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15 }, + { -38, -38, -38, -38, -38, -38, -38, -38, -38, 41, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38 }, + { -39, -39, -39, -39, -39, -39, -39, -39, -39, 41, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39 }, + { f, f, f, f, 46, f, f, f, f, f, f, 54, f, f, f, f, f, 56, 48, 50, 62, f, f, 64, f, f, f, 66, f, 68, f, f, f, f, 70, f, f, f, f, f, f, f, f, 52, 92, f, 58, f, f, f, f, 72, 60 }, + { f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 93 }, + { f, f, 6, f, 7, 8, f, f, f, f, f, f, 9, 10, 12, 13, 14, f, f, f, f, f, 15, f, f, f, 16, f, 17, f, 18, f, 32, f, f, f, f, f, f, f, 33, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 34, f, f, f, 35, f, f, 36, f, f, f, f, f, f, f, f, f, f, 39, f, 24, f, 40, 94, f, 43, 44 }, + { f, f, f, f, 46, f, -97, f, f, f, f, 54, f, f, f, f, f, 56, 48, 50, 62, f, f, 64, f, f, f, 66, f, 68, f, f, f, f, 70, f, f, f, f, f, f, f, f, 52, f, f, 58, -97, f, -97, f, 72, 60 }, + { f, f, f, f, f, f, 96, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 97, f, -93, f, f, f, f, f, f, 98 }, + { f, f, -98, f, -98, -98, f, f, f, f, -98, f, -98, -98, -98, -98, -98, f, f, f, f, f, -98, f, f, f, -98, f, -98, f, -98, f, -98, f, f, f, f, f, f, f, -98, f, f, f, f, f, f, f, f, -98 }, + { f, f, -99, f, -99, -99, f, f, f, f, -99, f, -99, -99, -99, -99, -99, f, f, f, f, f, -99, f, f, f, -99, f, -99, f, -99, f, -99, f, f, f, f, f, f, f, -99, f, f, f, f, f, f, f, f, -99 }, + { f, f, 6, f, 7, 8, f, f, f, f, 11, f, 9, 10, 12, 13, 14, f, f, f, f, f, 15, f, f, f, 16, f, 17, f, 18, f, 32, f, f, f, f, f, f, f, 33, f, f, f, f, f, f, f, f, -92, f, f, f, f, f, 34, f, 95, 99, 35, f, f, 36, f, f, f, f, f, f, f, f, f, f, 39, f, 100, f, 40, 103, f, 43, 44 }, + { f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, -94 }, + { -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, 101, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100 }, + { f, f, 6, f, 7, 8, f, f, f, f, f, f, 9, 10, 12, 13, 14, f, f, f, f, f, 15, f, f, f, 16, f, 17, f, 18, f, 32, f, f, f, f, f, f, f, 33, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 34, f, f, f, 35, f, f, 36, f, f, f, f, f, f, f, f, f, f, 39, f, 24, f, 40, 102, f, 43, 44 }, + { f, f, f, f, 46, f, -96, f, f, f, f, 54, f, f, f, f, f, 56, 48, 50, 62, f, f, 64, f, f, f, 66, f, 68, f, f, f, f, 70, f, f, f, f, f, f, f, f, 52, f, f, 58, -96, f, -96, f, 72, 60 }, + { f, f, f, f, 46, f, -95, f, f, f, f, 54, f, f, f, f, f, 56, 48, 50, 62, f, f, 64, f, f, f, 66, f, 68, f, f, f, f, 70, f, f, f, f, f, f, f, f, 52, f, f, 58, -95, f, -95, f, 72, 60 }, + { f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 105 }, + { -91, -91, f, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, f, f, f, -91, -91, -91, -91, -91, f, -91, f, -91, -91, -91, -91, -91, -91, -91, f, f, -91, -91, -91, -91, -91, -91, f, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91 }, + { f, f, f, f, 46, f, f, f, f, f, f, 54, f, f, f, f, f, 56, 48, 50, 62, f, f, 64, f, f, f, 66, f, 68, f, f, f, f, 70, f, f, f, f, f, f, f, f, 52, f, 107, 58, f, f, f, f, 72, 60 }, + { -70, -70, f, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, f, f, f, -70, -70, -70, -70, -70, f, -70, f, -70, -70, -70, -70, -70, -70, -70, f, f, -70, -70, -70, -70, -70, -70, f, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70 }, + { -37, -37, -37, -37, -37, -37, -37, -37, -37, 41, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37 }, + { f, f, f, f, 46, f, f, f, f, f, f, 54, f, f, f, f, f, 56, 48, 50, 62, f, f, 64, f, f, f, 66, f, 68, f, f, f, f, 70, f, f, f, f, f, f, f, 110, 52, f, f, 58, f, f, f, f, 72, 60 }, + { -55, f, f, f, f, f, -55, -55, -55, f, f, f, -55, f, f, f, f, f, f, f, f, -55, f, f, f, -55, -55, f, -55, f, -55, -55, f, f, f, f, -55, f, f, -55, f, f, -55, f, f, f, f, f, f, f, -55, f, f, f, f, f, f, f, f, f, 111, f, f, f, f, f, f, f, f, 4 }, + { f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 112 }, + { -74, -74, f, f, f, f, -74, -74, -74, f, f, f, -74, f, f, f, f, f, f, f, f, -74, f, f, f, -74, -74, f, -74, f, -74, -74, f, f, f, -74, -74, -74, -74, -74, f, f, -74, f, f, f, f, f, f, f, -74 }, + { -86, -86, f, f, f, f, -86, -86, -86, f, f, f, -86, f, f, f, f, f, f, f, f, -86, f, f, f, -86, -86, f, -86, f, -86, -86, f, f, f, -86, -86, -86, -86, -86, f, f, -86, f, f, f, f, f, f, f, -86 }, + { f, -59, 6, f, 7, 8, -59, f, f, f, f, f, 9, 10, 12, 13, 14, f, f, f, f, f, 15, f, f, f, 16, f, 17, f, 18, f, 32, f, f, -59, -59, -59, -59, f, 33, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 34, f, f, f, 35, f, f, 36, f, f, 76, f, 115, f, f, f, f, f, 39, f, 24, f, 40, 81, f, 43, 44 }, + { f, -46, f, f, f, f, 116, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, -46, -46, -46, -46 }, + { f, -47, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, -47, -47, -47, -47 }, + { -55, -55, f, f, f, f, -55, -55, -55, f, f, f, -55, f, f, f, f, f, f, f, f, -55, f, f, f, -55, -55, f, -55, f, -55, -55, f, f, f, f, f, f, f, -55, f, f, -55, f, f, f, f, f, f, f, -55, f, f, f, f, f, f, f, f, f, 118, f, f, f, f, f, f, f, f, 4 }, + { f, 119 }, + { f, f, 6, f, 7, 8, f, f, f, f, f, f, 9, 10, 12, 13, 14, f, f, f, f, f, 15, f, f, f, 16, f, 17, f, 18, f, 32, f, f, f, f, f, f, f, 33, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 34, f, f, f, 35, f, f, 36, f, f, f, f, f, f, f, f, f, f, 39, f, 24, f, 40, 120, f, 43, 44 }, + { -75, -75, f, f, 46, f, -75, -75, -75, f, f, 54, -75, f, f, f, f, 56, 48, 50, 62, -75, f, 64, f, -75, -75, 66, -75, 68, -75, -75, f, f, 70, -75, -75, -75, -75, -75, f, f, -75, 52, f, f, 58, f, f, f, -75, 72, 60 }, + { f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 16, f, 17, f, 122, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 127, f, f, f, f, f, f, f, 128, f, f, f, f, f, f, f, f, f, f, f, 24 }, + { f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 16, f, 17, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 123, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 24 }, + { f, f, f, f, f, f, f, f, f, f, f, f, 19, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 124 }, + { -55, f, f, f, f, f, -55, -55, -55, f, f, f, -55, f, f, f, f, f, f, f, f, -55, f, f, f, -55, -55, f, -55, f, -55, -55, f, f, f, f, -55, f, f, -55, f, f, -55, f, f, f, f, f, f, f, -55, f, f, f, f, f, f, f, f, f, 125, f, f, f, f, f, f, f, f, 4 }, + { f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 126 }, + { -83, -83, f, f, f, f, -83, -83, -83, f, f, f, -83, f, f, f, f, f, f, f, f, -83, f, f, f, -83, -83, f, -83, f, -83, -83, f, f, f, -83, -83, -83, -83, -83, f, f, -83, f, f, f, f, f, f, f, -83 }, + { -67, -67, f, f, f, f, -67, -67, -67, f, f, f, -67, f, f, f, f, f, f, f, f, -67, f, f, f, -67, -67, f, -67, f, -67, -67, f, -67, f, -67, -67, -67, -67, -67, f, f, -67, f, f, f, f, -67, f, f, -67 }, + { -81, -81, f, f, f, f, -81, -81, -81, f, f, f, -81, f, f, f, f, f, f, f, f, -81, f, f, f, -81, -81, f, -81, f, -81, -81, f, 129, f, -81, -81, -81, -81, -81, f, f, -81, f, f, f, f, 131, f, f, -81 }, + { f, f, 6, f, 7, 8, f, f, f, f, f, f, 9, 10, 12, 13, 14, f, f, f, f, f, 15, f, f, f, 16, f, 17, f, 18, f, 32, f, f, f, f, f, f, f, 33, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 34, f, f, f, 35, f, f, 36, f, f, 130, f, f, f, f, f, f, f, 39, f, 24, f, 40, 81, f, 43, 44 }, + { -82, -82, f, f, f, f, -82, -82, -82, f, f, f, -82, f, f, f, f, f, f, f, f, -82, f, f, f, -82, -82, f, -82, f, -82, -82, f, f, f, -82, -82, -82, -82, -82, f, f, -82, f, f, f, f, 77, f, f, -82 }, + { f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 16, f, 17, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 132, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 24 }, + { -68, -68, f, f, f, f, -68, -68, -68, f, f, f, -68, f, f, f, f, f, f, f, f, -68, f, f, -68, -68, -68, f, -68, f, -68, -68, f, -68, f, -68, -68, -68, -68, -68, f, f, -68, f, f, f, f, -68, f, f, -68 }, + { f, f, 6, f, 7, 8, f, f, f, f, f, f, 9, 10, 12, 13, 14, f, f, f, f, f, 15, f, f, f, 16, f, 17, f, 18, f, 32, f, f, f, f, f, f, f, 33, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 34, f, f, f, 35, f, f, 36, f, f, f, f, f, f, f, f, f, f, 39, f, 24, f, 40, 134, f, 43, 44 }, + { f, f, f, 135, 46, f, f, f, f, f, f, 54, f, f, f, f, f, 56, 48, 50, 62, f, f, 64, f, f, f, 66, f, 68, f, f, f, f, 70, f, f, f, f, f, f, f, f, 52, f, f, 58, f, f, f, f, 72, 60 }, + { -55, f, f, f, f, f, -55, -55, -55, f, f, f, -55, f, f, f, f, f, f, f, f, -55, f, f, f, -55, -55, f, -55, f, -55, -55, f, f, f, f, -55, -55, -55, -55, f, f, -55, f, f, f, f, f, f, f, -55, f, f, f, f, f, f, f, f, f, 136, f, f, f, f, f, f, f, f, 4 }, + { f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, -53, -53, -53, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 137 }, + { f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 138, 139, 143 }, + { -76, -76, f, f, f, f, -76, -76, -76, f, f, f, -76, f, f, f, f, f, f, f, f, -76, f, f, f, -76, -76, f, -76, f, -76, -76, f, f, f, -76, -76, -76, -76, -76, f, f, -76, f, f, f, f, f, f, f, -76 }, + { f, f, 6, f, 7, 8, f, f, f, f, f, f, 9, 10, 12, 13, 14, f, f, f, f, f, 15, f, f, f, 16, f, 17, f, 18, f, 32, f, f, f, f, f, f, f, 33, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 34, f, f, f, 35, f, f, 36, f, f, f, f, f, f, f, f, f, f, 39, f, 24, f, 40, 140, f, 43, 44 }, + { f, f, f, 141, 46, f, f, f, f, f, f, 54, f, f, f, f, f, 56, 48, 50, 62, f, f, 64, f, f, f, 66, f, 68, f, f, f, f, 70, f, f, f, f, f, f, f, f, 52, f, f, 58, f, f, f, f, 72, 60 }, + { -55, f, f, f, f, f, -55, -55, -55, f, f, f, -55, f, f, f, f, f, f, f, f, -55, f, f, f, -55, -55, f, -55, f, -55, -55, f, f, f, f, -55, -55, -55, -55, f, f, -55, f, f, f, f, f, f, f, -55, f, f, f, f, f, f, f, f, f, 142, f, f, f, f, f, f, f, f, 4 }, + { f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, -54, -54, -54 }, + { -55, f, f, f, f, f, -55, -55, -55, f, f, f, -55, f, f, f, f, f, f, f, f, -55, f, f, f, -55, -55, f, -55, f, -55, -55, f, f, f, f, -55, f, f, -55, f, f, -55, f, f, f, f, f, f, f, -55, f, f, f, f, f, f, f, f, f, 144, f, f, f, f, f, f, f, f, 4 }, + { f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 145 }, + { -77, -77, f, f, f, f, -77, -77, -77, f, f, f, -77, f, f, f, f, f, f, f, f, -77, f, f, f, -77, -77, f, -77, f, -77, -77, f, f, f, -77, -77, -77, -77, -77, f, f, -77, f, f, f, f, f, f, f, -77 }, + { f, f, f, f, f, -44, f, f, f, f, -44, f, -44, -44, f, f, f, f, f, f, f, f, f, f, f, f, 16, f, 17, f, f, f, f, -44, f, f, f, f, f, f, f, -44, f, f, f, f, f, -44, -44, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 147 }, + { -88, -88, f, f, f, f, -88, -88, -88, f, f, f, -88, f, f, f, f, f, f, f, f, -88, f, f, f, -88, -88, f, -88, f, -88, -88, f, f, f, -88, -88, -88, -88, -88, f, f, -88, f, f, f, f, f, f, f, -88 }, + { f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 16, f, 17, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 149, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 24, 150 }, + { f, f, f, f, f, f, f, f, f, f, f, f, -40, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, -40, f, f, f, f, f, f, -40 }, + { f, f, f, f, f, f, f, f, f, f, f, f, 19, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 151, f, f, f, f, f, f, 153, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 155 }, + { f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 152 }, + { f, f, f, f, f, f, f, f, f, f, f, f, -41, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, -41, f, f, f, f, f, f, -41 }, + { f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 154 }, + { f, f, f, f, f, f, f, f, f, f, f, f, -42, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, -42, f, f, f, f, f, f, -42 }, + { -55, f, f, f, f, f, -55, -55, -55, f, f, f, -55, f, f, f, f, f, f, f, f, -55, f, f, f, -55, -55, f, -55, f, -55, -55, f, f, f, f, -55, f, f, -55, f, f, -55, f, f, f, f, f, f, f, -55, f, f, f, f, f, f, f, f, f, 156, f, f, f, f, f, f, f, f, 4 }, + { f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 157 }, + { -84, -84, f, f, f, f, -84, -84, -84, f, f, f, -84, f, f, f, f, f, f, f, f, -84, f, f, f, -84, -84, f, -84, f, -84, -84, f, f, f, -84, -84, -84, -84, -84, f, f, -84, f, f, f, f, f, f, f, -84 }, + { f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 16, f, 17, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 159, f, f, f, f, f, f, f, 172, f, f, f, f, f, f, f, f, f, f, f, 24 }, + { f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, -67, f, f, f, f, f, f, f, f, 160, f, f, f, f, f, f, f, f, f, f, f, f, f, -67 }, + { f, f, 6, f, 7, 8, f, f, f, f, f, f, 9, 10, 12, 13, 14, f, f, f, f, f, 15, f, f, f, 16, f, 17, f, 18, f, 32, f, f, f, f, f, f, f, 33, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 34, f, f, f, 35, f, f, 36, f, f, f, f, f, f, f, f, f, f, 39, f, 24, f, 40, 161, f, 43, 44 }, + { f, f, f, f, 46, f, f, f, f, f, f, 54, f, f, f, f, f, 56, 48, 50, 62, f, f, 64, f, f, f, 66, f, 68, f, f, f, f, 70, f, f, f, f, f, f, f, f, 52, f, f, 58, 162, f, f, f, 72, 60 }, + { f, f, 6, f, 7, 8, f, f, f, f, f, f, 9, 10, 12, 13, 14, f, f, f, f, f, 15, f, f, f, 16, f, 17, f, 18, f, 32, f, f, f, f, f, f, f, 33, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 34, f, f, f, 35, f, f, 36, f, f, f, f, f, f, f, f, f, f, 39, f, 24, f, 40, 163, f, 43, 44 }, + { f, f, f, f, 46, f, f, f, f, f, f, 54, f, f, f, f, f, 56, 48, 50, 62, f, f, 64, f, f, f, 66, f, 68, f, f, f, f, 70, f, f, f, f, f, f, f, 164, 52, f, f, 58, 167, f, f, f, 72, 60 }, + { -55, f, f, f, f, f, -55, -55, -55, f, f, f, -55, f, f, f, f, f, f, f, f, -55, f, f, f, -55, -55, f, -55, f, -55, -55, f, f, f, f, -55, f, f, -55, f, f, -55, f, f, f, f, f, f, f, -55, f, f, f, f, f, f, f, f, f, 165, f, f, f, f, f, f, f, f, 4 }, + { f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 166 }, + { -78, -78, f, f, f, f, -78, -78, -78, f, f, f, -78, f, f, f, f, f, f, f, f, -78, f, f, f, -78, -78, f, -78, f, -78, -78, f, f, f, -78, -78, -78, -78, -78, f, f, -78, f, f, f, f, f, f, f, -78 }, + { f, f, 6, f, 7, 8, f, f, f, f, f, f, 9, 10, 12, 13, 14, f, f, f, f, f, 15, f, f, f, 16, f, 17, f, 18, f, 32, f, f, f, f, f, f, f, 33, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 34, f, f, f, 35, f, f, 36, f, f, f, f, f, f, f, f, f, f, 39, f, 24, f, 40, 168, f, 43, 44 }, + { f, f, f, f, 46, f, f, f, f, f, f, 54, f, f, f, f, f, 56, 48, 50, 62, f, f, 64, f, f, f, 66, f, 68, f, f, f, f, 70, f, f, f, f, f, f, f, 169, 52, f, f, 58, f, f, f, f, 72, 60 }, + { -55, f, f, f, f, f, -55, -55, -55, f, f, f, -55, f, f, f, f, f, f, f, f, -55, f, f, f, -55, -55, f, -55, f, -55, -55, f, f, f, f, -55, f, f, -55, f, f, -55, f, f, f, f, f, f, f, -55, f, f, f, f, f, f, f, f, f, 170, f, f, f, f, f, f, f, f, 4 }, + { f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 171 }, + { -79, -79, f, f, f, f, -79, -79, -79, f, f, f, -79, f, f, f, f, f, f, f, f, -79, f, f, f, -79, -79, f, -79, f, -79, -79, f, f, f, -79, -79, -79, -79, -79, f, f, -79, f, f, f, f, f, f, f, -79 }, + { f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 173, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 131 }, + { f, f, 6, f, 7, 8, f, f, f, f, f, f, 9, 10, 12, 13, 14, f, f, f, f, f, 15, f, f, f, 16, f, 17, f, 18, f, 32, f, f, f, f, f, f, f, 33, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 34, f, f, f, 35, f, f, 36, f, f, 174, f, f, f, f, f, f, f, 39, f, 24, f, 40, 81, f, 43, 44 }, + { f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 175, f, f, f, f, 77 }, + { -55, f, f, f, f, f, -55, -55, -55, f, f, f, -55, f, f, f, f, f, f, f, f, -55, f, f, f, -55, -55, f, -55, f, -55, -55, f, f, f, f, -55, f, f, -55, f, f, -55, f, f, f, f, f, f, f, -55, f, f, f, f, f, f, f, f, f, 176, f, f, f, f, f, f, f, f, 4 }, + { f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 177 }, + { -80, -80, f, f, f, f, -80, -80, -80, f, f, f, -80, f, f, f, f, f, f, f, f, -80, f, f, f, -80, -80, f, -80, f, -80, -80, f, f, f, -80, -80, -80, -80, -80, f, f, -80, f, f, f, f, f, f, f, -80 }, + { f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 16, f, 17, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 179 }, + { f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 180 }, + { -89, -89, f, f, f, f, -89, -89, -89, f, f, f, -89, f, f, f, f, f, f, f, f, -89, f, f, f, -89, -89, f, -89, f, -89, -89, f, f, f, -89, -89, -89, -89, -89, f, f, -89, f, f, f, f, f, f, f, -89 }, + { -55, f, f, f, f, f, -55, -55, -55, f, f, f, -55, f, f, f, f, f, f, f, f, -55, f, f, f, -55, -55, f, -55, f, -55, -55, f, f, f, f, -55, f, f, -55, f, f, -55, f, f, f, f, f, f, f, -55, f, f, f, f, f, f, f, f, f, 182, f, f, f, f, f, f, f, f, 4 }, + { f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 183 }, + { -72, -72, f, f, f, f, -72, -72, -72, f, f, f, -72, f, f, f, f, f, f, f, f, -72, f, f, f, -72, -72, f, -72, f, -72, -72, f, f, f, -72, -72, -72, -72, -72, f, f, -72, f, f, f, f, f, f, f, -72 }, + { -87, -87, f, f, f, f, -87, -87, -87, f, f, f, -87, f, f, f, f, f, f, f, f, -87, f, f, f, -87, -87, f, -87, f, -87, -87, f, f, f, -87, -87, -87, -87, -87, f, f, -87, f, f, f, f, f, f, f, -87 }, + { -56, -56, f, f, f, f, -56, -56, -56, f, f, f, -56, f, f, f, f, f, f, f, f, -56, f, f, f, -56, -56, f, -56, f, -56, -56, f, f, f, -56, -56, -56, -56, -56, f, f, -56, f, f, f, f, f, f, f, -56 }, + { f, f, f, f, f, 37, f, f, f, f, 38, f, 75, 10, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 82, f, f, f, f, f, f, 84, f, f, f, f, f, f, f, f, f, f, 86, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 88 }, + { f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 188, f, f, f, f, f, f, f, f, f, f, f, f, f, 190 }, + { f, f, 6, f, 7, 8, f, f, f, f, f, f, 9, 10, 12, 13, 14, f, f, f, f, f, 15, f, f, f, 16, f, 17, f, 18, f, 32, f, f, f, f, f, f, f, 33, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 34, f, f, f, 35, f, f, 36, f, f, 189, f, f, f, f, f, f, f, 39, f, 24, f, 40, 81, f, 43, 44 }, + { -73, -73, f, f, f, f, -73, -73, -73, f, f, f, -73, f, f, f, f, f, f, f, f, -73, f, f, f, -73, -73, f, -73, f, -73, -73, f, f, f, -73, -73, -73, -73, -73, f, f, -73, f, f, f, f, 77, f, f, -73 }, + { f, f, f, f, f, f, f, f, f, f, f, f, 9, f, f, f, f, f, f, f, f, f, f, f, f, f, 16, f, 17, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 34, f, f, f, f, f, f, 186, f, f, f, f, f, f, f, f, f, f, 191, f, 24, f, f, f, f, 43 }, + { f, f, f, f, f, -69, f, f, f, f, -69, f, -69, -69, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, -66, f, f, f, f, f, f, f, -69, f, f, f, f, f, -66, -69 }, + { f, f, f, f, f, -69, f, f, f, f, -69, f, -69, -69, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, -65, f, f, f, f, f, f, f, -69, f, f, f, f, f, -65, -69 }, + { -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90 }, + { -85, -85, f, f, f, -71, -85, -85, -85, f, -71, f, -71, -71, f, f, f, f, f, f, f, -85, f, f, f, -85, -85, f, -85, f, -85, -85, f, f, f, -85, -85, -85, -85, -85, f, -71, -85, f, f, f, f, f, -71, f, -85 }, + { f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, -1 }, + { f, f, 6, f, 7, 8, f, f, f, f, f, f, 9, 10, 12, 13, 14, f, f, f, f, f, 15, f, f, f, 16, f, 17, f, 18, f, 32, f, f, f, f, f, f, f, 33, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 34, f, f, f, 35, f, f, 36, f, f, 197, f, f, f, f, f, 199, f, 39, f, 24, f, 40, 81, f, 43, 44 }, + { f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, 198, f, f, f, f, f, f, f, f, f, f, f, 77 }, + { f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, -52 }, + { f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, -2 }, +} + +--- Run the parser across a sequence of tokens. +-- +-- @tparam table context The current parser context. +-- @tparam function get_next A stateful function which returns the next token. +-- @treturn boolean Whether the parse succeeded or not. +local function parse(context, get_next, start) + local stack, stack_n = { start or 1, 1, 1 }, 1 + local reduce_stack = {} + + while true do + local token, token_start, token_end = get_next() + local state = stack[stack_n] + local action = transitions[state][token] + + if not action then -- Error + return handle_error(context, stack, stack_n, token, token_start, token_end) + elseif action >= 0 then -- Shift + stack_n = stack_n + 3 + stack[stack_n], stack[stack_n + 1], stack[stack_n + 2] = action, token_start, token_end + elseif action >= -2 then -- Accept + return true + else -- Reduce + -- Reduction is quite complex to get right, as the error code expects the parser + -- to be shifting rather than reducing. Menhir achieves this by making the parser + -- stack be immutable, but that's hard to do efficiently in Lua: instead we track + -- what symbols we've pushed/popped, and only perform this change when we're ready + -- to shift again. + + local popped, pushed = 0, 0 + while true do + -- Look at the current item to reduce + local reduce = productions[-action] + local terminal, to_pop = reduce[1], reduce[2] + + -- Find the state at the start of this production. If to_pop == 0 + -- then use the current state. + local lookback = state + if to_pop > 0 then + pushed = pushed - to_pop + if pushed <= 0 then + -- If to_pop >= pushed, then clear the reduction stack + -- and consult the normal stack. + popped = popped - pushed + pushed = 0 + lookback = stack[stack_n - popped * 3] + else + -- Otherwise consult the stack of temporary reductions. + lookback = reduce_stack[pushed] + end + end + + state = transitions[lookback][terminal] + if not state or state <= 0 then error("reduce must shift!") end + + -- And fetch the next action + action = transitions[state][token] + + if not action then -- Error + return handle_error(context, stack, stack_n, token, token_start, token_end) + elseif action >= 0 then -- Shift + break + elseif action >= -2 then -- Accept + return true + else + pushed = pushed + 1 + reduce_stack[pushed] = state + end + end + + if popped == 1 and pushed == 0 then + -- Handle the easy case: Popped one item and replaced it with another + stack[stack_n] = state + else + -- Otherwise pop and push. + -- FIXME: The positions of everything here are entirely wrong. + local end_pos = stack[stack_n + 2] + stack_n = stack_n - popped * 3 + local start_pos = stack[stack_n + 1] + + for i = 1, pushed do + stack_n = stack_n + 3 + stack[stack_n], stack[stack_n + 1], stack[stack_n + 2] = reduce_stack[i], end_pos, end_pos + end + + stack_n = stack_n + 3 + stack[stack_n], stack[stack_n + 1], stack[stack_n + 2] = state, start_pos, end_pos + end + + -- Shift the token onto the stack + stack_n = stack_n + 3 + stack[stack_n], stack[stack_n + 1], stack[stack_n + 2] = action, token_start, token_end + end + end +end + +return { + tokens = tokens, + parse = parse, + repl_exprs = 196, --[[- The repl_exprs starting state. ]] + program = 1, --[[- The program starting state. ]] +} diff --git a/Bin/PicoCalc SD/cc/pretty.lua b/Bin/PicoCalc SD/cc/pretty.lua new file mode 100644 index 0000000..a1c5958 --- /dev/null +++ b/Bin/PicoCalc SD/cc/pretty.lua @@ -0,0 +1,526 @@ +-- SPDX-FileCopyrightText: 2020 The CC: Tweaked Developers +-- +-- SPDX-License-Identifier: MPL-2.0 + +--[[- A pretty printer for rendering data structures in an aesthetically +pleasing manner. + +In order to display something using [`cc.pretty`], you build up a series of +[documents][`Doc`]. These behave a little bit like strings; you can concatenate +them together and then print them to the screen. + +However, documents also allow you to control how they should be printed. There +are several functions (such as [`nest`] and [`group`]) which allow you to control +the "layout" of the document. When you come to display the document, the 'best' +(most compact) layout is used. + +The structure of this module is based on [A Prettier Printer][prettier]. + +[prettier]: https://homepages.inf.ed.ac.uk/wadler/papers/prettier/prettier.pdf "A Prettier Printer" + +@module cc.pretty +@since 1.87.0 +@usage Print a table to the terminal + + local pretty = require "cc.pretty" + pretty.pretty_print({ 1, 2, 3 }) + +@usage Build a custom document and display it + + local pretty = require "cc.pretty" + pretty.print(pretty.group(pretty.text("hello") .. pretty.space_line .. pretty.text("world"))) +]] + +local expect = require "cc.expect" +local expect, field = expect.expect, expect.field + +local type, getmetatable, setmetatable, colors, str_write, tostring = type, getmetatable, setmetatable, colors, write, tostring +local debug_info, debug_local = debug.getinfo, debug.getlocal + +--- [`table.insert`] alternative, but with the length stored inline. +local function append(out, value) + local n = out.n + 1 + out[n], out.n = value, n +end + +--- A document containing formatted text, with multiple possible layouts. +-- +-- Documents effectively represent a sequence of strings in alternative layouts, +-- which we will try to print in the most compact form necessary. +-- +-- @type Doc +local Doc = { } + +local function mk_doc(tbl) return setmetatable(tbl, Doc) end + +--- An empty document. +local empty = mk_doc({ tag = "nil" }) + +--- A document with a single space in it. +local space = mk_doc({ tag = "text", text = " " }) + +--- A line break. When collapsed with [`group`], this will be replaced with [`empty`]. +local line = mk_doc({ tag = "line", flat = empty }) + +--- A line break. When collapsed with [`group`], this will be replaced with [`space`]. +local space_line = mk_doc({ tag = "line", flat = space }) + +local text_cache = { [""] = empty, [" "] = space, ["\n"] = space_line } + +local function mk_text(text, color) + return text_cache[text] or setmetatable({ tag = "text", text = text, color = color }, Doc) +end + +--- Create a new document from a string. +-- +-- If your string contains multiple lines, [`group`] will flatten the string +-- into a single line, with spaces between each line. +-- +-- @tparam string text The string to construct a new document with. +-- @tparam[opt] number color The color this text should be printed with. If not given, we default to the current +-- color. +-- @treturn Doc The document with the provided text. +-- @usage Write some blue text. +-- +-- local pretty = require "cc.pretty" +-- pretty.print(pretty.text("Hello!", colors.blue)) +local function text(text, color) + expect(1, text, "string") + expect(2, color, "number", "nil") + + local cached = text_cache[text] + if cached then return cached end + + local new_line = text:find("\n", 1) + if not new_line then return mk_text(text, color) end + + -- Split the string by "\n". With a micro-optimisation to skip empty strings. + local doc = setmetatable({ tag = "concat", n = 0 }, Doc) + if new_line ~= 1 then append(doc, mk_text(text:sub(1, new_line - 1), color)) end + + new_line = new_line + 1 + while true do + local next_line = text:find("\n", new_line) + append(doc, space_line) + if not next_line then + if new_line <= #text then append(doc, mk_text(text:sub(new_line), color)) end + return doc + else + if new_line <= next_line - 1 then + append(doc, mk_text(text:sub(new_line, next_line - 1), color)) + end + new_line = next_line + 1 + end + end +end + +--- Concatenate several documents together. This behaves very similar to string concatenation. +-- +-- @tparam Doc|string ... The documents to concatenate. +-- @treturn Doc The concatenated documents. +-- @usage +-- local pretty = require "cc.pretty" +-- local doc1, doc2 = pretty.text("doc1"), pretty.text("doc2") +-- print(pretty.concat(doc1, " - ", doc2)) +-- print(doc1 .. " - " .. doc2) -- Also supports .. +local function concat(...) + local args = table.pack(...) + for i = 1, args.n do + if type(args[i]) == "string" then args[i] = text(args[i]) end + if getmetatable(args[i]) ~= Doc then expect(i, args[i], "document") end + end + + if args.n == 0 then return empty end + if args.n == 1 then return args[1] end + + args.tag = "concat" + return setmetatable(args, Doc) +end + +Doc.__concat = concat --- @local + +--- Indent later lines of the given document with the given number of spaces. +-- +-- For instance, nesting the document +-- ```txt +-- foo +-- bar +-- ``` +-- by two spaces will produce +-- ```txt +-- foo +-- bar +-- ``` +-- +-- @tparam number depth The number of spaces with which the document should be indented. +-- @tparam Doc doc The document to indent. +-- @treturn Doc The nested document. +-- @usage +-- local pretty = require "cc.pretty" +-- print(pretty.nest(2, pretty.text("foo\nbar"))) +local function nest(depth, doc) + expect(1, depth, "number") + if getmetatable(doc) ~= Doc then expect(2, doc, "document") end + if depth <= 0 then error("depth must be a positive number", 2) end + + return setmetatable({ tag = "nest", depth = depth, doc }, Doc) +end + +local function flatten(doc) + if doc.flat then return doc.flat end + + local kind = doc.tag + if kind == "nil" or kind == "text" then + return doc + elseif kind == "concat" then + local out = setmetatable({ tag = "concat", n = doc.n }, Doc) + for i = 1, doc.n do out[i] = flatten(doc[i]) end + doc.flat, out.flat = out, out -- cache the flattened node + return out + elseif kind == "nest" then + return flatten(doc[1]) + elseif kind == "group" then + return doc[1] + else + error("Unknown doc " .. kind) + end +end + +--- Builds a document which is displayed on a single line if there is enough +-- room, or as normal if not. +-- +-- @tparam Doc doc The document to group. +-- @treturn Doc The grouped document. +-- @usage Uses group to show things being displayed on one or multiple lines. +-- +-- local pretty = require "cc.pretty" +-- local doc = pretty.group("Hello" .. pretty.space_line .. "World") +-- print(pretty.render(doc, 5)) -- On multiple lines +-- print(pretty.render(doc, 20)) -- Collapsed onto one. +local function group(doc) + if getmetatable(doc) ~= Doc then expect(1, doc, "document") end + + if doc.tag == "group" then return doc end -- Skip if already grouped. + + local flattened = flatten(doc) + if flattened == doc then return doc end -- Also skip if flattening does nothing. + return setmetatable({ tag = "group", flattened, doc }, Doc) +end + +local function get_remaining(doc, width) + local kind = doc.tag + if kind == "nil" or kind == "line" then + return width + elseif kind == "text" then + return width - #doc.text + elseif kind == "concat" then + for i = 1, doc.n do + width = get_remaining(doc[i], width) + if width < 0 then break end + end + return width + elseif kind == "group" or kind == "nest" then + return get_remaining(kind[1]) + else + error("Unknown doc " .. kind) + end +end + +--- Display a document on the terminal. +-- +-- @tparam Doc doc The document to render +-- @tparam[opt=0.6] number ribbon_frac The maximum fraction of the width that we should write in. +local function write(doc, ribbon_frac) + if getmetatable(doc) ~= Doc then expect(1, doc, "document") end + expect(2, ribbon_frac, "number", "nil") + + local term = term + local width, height = term.getSize() + local ribbon_width = (ribbon_frac or 0.6) * width + if ribbon_width < 0 then ribbon_width = 0 end + if ribbon_width > width then ribbon_width = width end + + local def_color = term.getTextColour() + local current_color = def_color + + local function go(doc, indent, col) + local kind = doc.tag + if kind == "nil" then + return col + elseif kind == "text" then + local doc_color = doc.color or def_color + if doc_color ~= current_color then + term.setTextColour(doc_color) + current_color = doc_color + end + + str_write(doc.text) + + return col + #doc.text + elseif kind == "line" then + local _, y = term.getCursorPos() + if y < height then + term.setCursorPos(indent + 1, y + 1) + else + term.scroll(1) + term.setCursorPos(indent + 1, height) + end + + return indent + elseif kind == "concat" then + for i = 1, doc.n do col = go(doc[i], indent, col) end + return col + elseif kind == "nest" then + return go(doc[1], indent + doc.depth, col) + elseif kind == "group" then + if get_remaining(doc[1], math.min(width, ribbon_width + indent) - col) >= 0 then + return go(doc[1], indent, col) + else + return go(doc[2], indent, col) + end + else + error("Unknown doc " .. kind) + end + end + + local col = math.max(term.getCursorPos() - 1, 0) + go(doc, 0, col) + if current_color ~= def_color then term.setTextColour(def_color) end +end + +--- Display a document on the terminal with a trailing new line. +-- +-- @tparam Doc doc The document to render. +-- @tparam[opt=0.6] number ribbon_frac The maximum fraction of the width that we should write in. +local function print(doc, ribbon_frac) + if getmetatable(doc) ~= Doc then expect(1, doc, "document") end + expect(2, ribbon_frac, "number", "nil") + write(doc, ribbon_frac) + str_write("\n") +end + +--- Render a document, converting it into a string. +-- +-- @tparam Doc doc The document to render. +-- @tparam[opt] number width The maximum width of this document. Note that long strings will not be wrapped to fit +-- this width - it is only used for finding the best layout. +-- @tparam[opt=0.6] number ribbon_frac The maximum fraction of the width that we should write in. +-- @treturn string The rendered document as a string. +local function render(doc, width, ribbon_frac) + if getmetatable(doc) ~= Doc then expect(1, doc, "document") end + expect(2, width, "number", "nil") + expect(3, ribbon_frac, "number", "nil") + + local ribbon_width + if width then + ribbon_width = (ribbon_frac or 0.6) * width + if ribbon_width < 0 then ribbon_width = 0 end + if ribbon_width > width then ribbon_width = width end + end + + local out = { n = 0 } + local function go(doc, indent, col) + local kind = doc.tag + if kind == "nil" then + return col + elseif kind == "text" then + append(out, doc.text) + return col + #doc.text + elseif kind == "line" then + append(out, "\n" .. (" "):rep(indent)) + return indent + elseif kind == "concat" then + for i = 1, doc.n do col = go(doc[i], indent, col) end + return col + elseif kind == "nest" then + return go(doc[1], indent + doc.depth, col) + elseif kind == "group" then + if not width or get_remaining(doc[1], math.min(width, ribbon_width + indent) - col) >= 0 then + return go(doc[1], indent, col) + else + return go(doc[2], indent, col) + end + else + error("Unknown doc " .. kind) + end + end + + go(doc, 0, 0) + return table.concat(out, "", 1, out.n) +end + +Doc.__tostring = render --- @local + +local keywords = { + ["and"] = true, ["break"] = true, ["do"] = true, ["else"] = true, + ["elseif"] = true, ["end"] = true, ["false"] = true, ["for"] = true, + ["function"] = true, ["if"] = true, ["in"] = true, ["local"] = true, + ["nil"] = true, ["not"] = true, ["or"] = true, ["repeat"] = true, ["return"] = true, + ["then"] = true, ["true"] = true, ["until"] = true, ["while"] = true, + } + +local comma = text(",") +local braces = text("{}") +local obrace, cbrace = text("{"), text("}") +local obracket, cbracket = text("["), text("] = ") + +local function key_compare(a, b) + local ta, tb = type(a), type(b) + + if ta == "string" then return tb ~= "string" or a < b + elseif tb == "string" then return false + end + + if ta == "number" then return tb ~= "number" or a < b end + return false +end + +local function show_function(fn, options) + local info = debug_info and debug_info(fn, "Su") + + -- Include function source position if available + local name + if options.function_source and info and info.short_src and info.linedefined and info.linedefined >= 1 then + name = "function<" .. info.short_src .. ":" .. info.linedefined .. ">" + else + name = tostring(fn) + end + + -- Include arguments if a Lua function and if available. Lua will report "C" + -- functions as variadic. + if options.function_args and info and info.what == "Lua" and info.nparams and debug_local then + local args = {} + for i = 1, info.nparams do args[i] = debug_local(fn, i) or "?" end + if info.isvararg then args[#args + 1] = "..." end + name = name .. "(" .. table.concat(args, ", ") .. ")" + end + + return name +end + +local function pretty_impl(obj, options, tracking) + local obj_type = type(obj) + if obj_type == "string" then + local formatted = ("%q"):format(obj):gsub("\\\n", "\\n") + return text(formatted, colors.red) + elseif obj_type == "number" then + return text(tostring(obj), colors.magenta) + elseif obj_type == "function" then + return text(show_function(obj, options), colors.lightGrey) + elseif obj_type ~= "table" or tracking[obj] then + return text(tostring(obj), colors.lightGrey) + elseif getmetatable(obj) ~= nil and getmetatable(obj).__tostring then + return text(tostring(obj)) + elseif next(obj) == nil then + return braces + else + tracking[obj] = true + local doc = setmetatable({ tag = "concat", n = 1, space_line }, Doc) + + local length, keys, keysn = #obj, {}, 1 + for k in pairs(obj) do + if type(k) ~= "number" or k % 1 ~= 0 or k < 1 or k > length then + keys[keysn], keysn = k, keysn + 1 + end + end + table.sort(keys, key_compare) + + for i = 1, length do + if i > 1 then append(doc, comma) append(doc, space_line) end + append(doc, pretty_impl(obj[i], options, tracking)) + end + + for i = 1, keysn - 1 do + if i > 1 or length >= 1 then append(doc, comma) append(doc, space_line) end + + local k = keys[i] + local v = obj[k] + if type(k) == "string" and not keywords[k] and k:match("^[%a_][%a%d_]*$") then + append(doc, text(k .. " = ")) + append(doc, pretty_impl(v, options, tracking)) + else + append(doc, obracket) + append(doc, pretty_impl(k, options, tracking)) + append(doc, cbracket) + append(doc, pretty_impl(v, options, tracking)) + end + end + + tracking[obj] = nil + return group(concat(obrace, nest(2, concat(table.unpack(doc, 1, doc.n))), space_line, cbrace)) + end +end + +--- Pretty-print an arbitrary object, converting it into a document. +-- +-- This can then be rendered with [`write`] or [`print`]. +-- +-- @param obj The object to pretty-print. +-- @tparam[opt] { function_args = boolean, function_source = boolean } options +-- Controls how various properties are displayed. +-- - `function_args`: Show the arguments to a function if known (`false` by default). +-- - `function_source`: Show where the function was defined, instead of +-- `function: xxxxxxxx` (`false` by default). +-- @treturn Doc The object formatted as a document. +-- @changed 1.88.0 Added `options` argument. +-- @usage Display a table on the screen +-- +-- local pretty = require "cc.pretty" +-- pretty.print(pretty.pretty({ 1, 2, 3 })) +-- @see pretty_print for a shorthand to prettify and print an object. +local function pretty(obj, options) + expect(2, options, "table", "nil") + options = options or {} + + local actual_options = { + function_source = field(options, "function_source", "boolean", "nil") or false, + function_args = field(options, "function_args", "boolean", "nil") or false, + } + return pretty_impl(obj, actual_options, {}) +end + +--[[- A shortcut for calling [`pretty`] and [`print`] together. + +@param obj The object to pretty-print. +@tparam[opt] { function_args = boolean, function_source = boolean } options +Controls how various properties are displayed. + - `function_args`: Show the arguments to a function if known (`false` by default). + - `function_source`: Show where the function was defined, instead of + `function: xxxxxxxx` (`false` by default). +@tparam[opt=0.6] number ribbon_frac The maximum fraction of the width that we should write in. + +@usage Display a table on the screen. + + local pretty = require "cc.pretty" + pretty.pretty_print({ 1, 2, 3 }) + +@see pretty +@see print +@since 1.99 +]] +local function pretty_print(obj, options, ribbon_frac) + expect(2, options, "table", "nil") + options = options or {} + expect(3, ribbon_frac, "number", "nil") + + return print(pretty(obj, options), ribbon_frac) +end + +return { + empty = empty, + space = space, + line = line, + space_line = space_line, + text = text, + concat = concat, + nest = nest, + group = group, + + write = write, + print = print, + render = render, + + pretty = pretty, + + pretty_print = pretty_print, +} diff --git a/Bin/PicoCalc SD/firmware/Lua_180a58e.bin b/Bin/PicoCalc SD/firmware/Lua_180a58e.bin deleted file mode 100644 index 1bd0d60..0000000 Binary files a/Bin/PicoCalc SD/firmware/Lua_180a58e.bin and /dev/null differ diff --git a/Bin/PicoCalc SD/firmware/MP3player_v0.5.bin b/Bin/PicoCalc SD/firmware/MP3player_v0.5.bin deleted file mode 100644 index 11e3f29..0000000 Binary files a/Bin/PicoCalc SD/firmware/MP3player_v0.5.bin and /dev/null differ diff --git a/Bin/PicoCalc SD/firmware/MicroPython_fa8b24c.bin b/Bin/PicoCalc SD/firmware/MicroPython_fa8b24c.bin deleted file mode 100644 index b38f350..0000000 Binary files a/Bin/PicoCalc SD/firmware/MicroPython_fa8b24c.bin and /dev/null differ diff --git a/Bin/PicoCalc SD/firmware/PicoCalc_Bootloader_v0.5.uf2 b/Bin/PicoCalc SD/firmware/PicoCalc_Bootloader_v0.5.uf2 deleted file mode 100644 index 1e1b544..0000000 Binary files a/Bin/PicoCalc SD/firmware/PicoCalc_Bootloader_v0.5.uf2 and /dev/null differ diff --git a/Bin/PicoCalc SD/firmware/PicoCalc_Fuzix_v1.0.img b/Bin/PicoCalc SD/firmware/PicoCalc_Fuzix_v1.0.img deleted file mode 100644 index aae133e..0000000 Binary files a/Bin/PicoCalc SD/firmware/PicoCalc_Fuzix_v1.0.img and /dev/null differ diff --git a/Bin/PicoCalc SD/firmware/PicoCalc_NES_v1.0.bin b/Bin/PicoCalc SD/firmware/PicoCalc_NES_v1.0.bin deleted file mode 100644 index a7b9673..0000000 Binary files a/Bin/PicoCalc SD/firmware/PicoCalc_NES_v1.0.bin and /dev/null differ diff --git a/Bin/PicoCalc SD/firmware/PicoCalc_PicoMite_v1.0.uf2 b/Bin/PicoCalc SD/firmware/PicoCalc_PicoMite_v1.0.uf2 deleted file mode 100644 index 77e7690..0000000 Binary files a/Bin/PicoCalc SD/firmware/PicoCalc_PicoMite_v1.0.uf2 and /dev/null differ diff --git a/Bin/PicoCalc SD/firmware/PicoMite_cbf6d71.bin b/Bin/PicoCalc SD/firmware/PicoMite_cbf6d71.bin deleted file mode 100644 index a904fa5..0000000 Binary files a/Bin/PicoCalc SD/firmware/PicoMite_cbf6d71.bin and /dev/null differ diff --git a/Bin/PicoCalc SD/firmware/uLisp_4.8f.bin b/Bin/PicoCalc SD/firmware/uLisp_4.8f.bin deleted file mode 100644 index 297cd4a..0000000 Binary files a/Bin/PicoCalc SD/firmware/uLisp_4.8f.bin and /dev/null differ diff --git a/Bin/PicoCalc SD/fonts/6x10.fnt b/Bin/PicoCalc SD/fonts/6x10.fnt new file mode 100644 index 0000000..0bb3fb0 Binary files /dev/null and b/Bin/PicoCalc SD/fonts/6x10.fnt differ diff --git a/Bin/PicoCalc SD/fonts/Acer8x8.fnt b/Bin/PicoCalc SD/fonts/Acer8x8.fnt new file mode 100644 index 0000000..e08b5d9 Binary files /dev/null and b/Bin/PicoCalc SD/fonts/Acer8x8.fnt differ diff --git a/Bin/PicoCalc SD/fonts/HP6x8.fnt b/Bin/PicoCalc SD/fonts/HP6x8.fnt new file mode 100644 index 0000000..6745aa5 Binary files /dev/null and b/Bin/PicoCalc SD/fonts/HP6x8.fnt differ diff --git a/Bin/PicoCalc SD/fonts/HP8x8.fnt b/Bin/PicoCalc SD/fonts/HP8x8.fnt new file mode 100644 index 0000000..bb1f526 Binary files /dev/null and b/Bin/PicoCalc SD/fonts/HP8x8.fnt differ diff --git a/Bin/PicoCalc SD/fonts/Haxor12.fnt b/Bin/PicoCalc SD/fonts/Haxor12.fnt new file mode 100644 index 0000000..94b3c2a Binary files /dev/null and b/Bin/PicoCalc SD/fonts/Haxor12.fnt differ diff --git a/Bin/PicoCalc SD/fonts/ProggyClean.fnt b/Bin/PicoCalc SD/fonts/ProggyClean.fnt new file mode 100644 index 0000000..80b3c28 Binary files /dev/null and b/Bin/PicoCalc SD/fonts/ProggyClean.fnt differ diff --git a/Bin/PicoCalc SD/lua/asteroids.lua b/Bin/PicoCalc SD/lua/asteroids.lua new file mode 100644 index 0000000..c04ca2a --- /dev/null +++ b/Bin/PicoCalc SD/lua/asteroids.lua @@ -0,0 +1,260 @@ +-- asteroids game +-- written by maple "mavica" syrup + +local poly_ship = {{0, -10}, {5, 5}, {0, 0}, {-5, 5}, {0, -10}} +local poly_thrust = {{-2.5, 2,5}, {0, 4}, {2.5, 2.5}} +local poly_shot = {{0, 0}, {0, -5}} +local poly_rock = {{-1,0},{-0.8,-0.6},{-0.4,-0.5}, +{0,-1},{0.5,-0.9},{0.3,-0.4},{0.8,-0.5}, +{1,0},{0.7,0.4},{0.7,0.6},{0.3,0.8},{0,0.7}, +{-0.3,0.9},{-0.7,0.8},{-0.7,0.5},{-1,0}} + +local bg = colors.fromRGB(0,0,0) +local fg = colors.fromRGB(0,255,0) +local width = 320 +local height = 320 + +local max_shots = 5 +local shot_speed = 7 +local rock_speed = 0.6 +local rock_size = 30 +local rock_step = 10 + +local player = { + x=width/2, + y=height/2, + accel = 0.06, + vx=0, + vy=0, + ang=0, + cdown=0, + inv=0 +} + +local shots = {} +local rocks = {} +local score = 0 +local lives = 3 + +local last_frame = 0 +local game_over = false + +local function sign(number) + return (number > 0 and 1) or (number == 0 and 0) or -1 +end + +local function distance(x1, y1, x2, y2) + return math.sqrt((x2-x1)^2+(y2-y1)^2) +end + +local function draw_rotated(shape, x, y, ang, scale, color) + processed = {} + + local sin = math.sin(ang) * scale + local cos = math.cos(ang) * scale + + for _,v in pairs(shape) do + local px = x + v[1] * cos - v[2] * sin + local py = y + v[1] * sin + v[2] * cos + table.insert(processed,math.floor(px+0.5)) + table.insert(processed,math.floor(py+0.5)) + end + + draw.polygon(processed, color) +end + +local shot_meta = { + erase = function(self) + draw_rotated(poly_shot, self.x, self.y, self.ang, 1, bg) + end, + update = function(self, k) + self.x = self.x + self.vx + self.y = self.y + self.vy + + if self.x > width or self.x < 0 or self.y > height or self.y < 0 then + self:erase() + table.remove(shots,k) + else + for rockid,rock in pairs(rocks) do + if distance(self.x, self.y, rock.x, rock.y) < rock.scale then + rock:hit(rockid,self,k) + end + end + end + end, + draw = function(self) + draw_rotated(poly_shot, self.x, self.y, self.ang, 1, fg) + end +} + +local rock_meta = {} --hoist + +local rock_create = function(x, y, ang, size) + rock = { + x = x, + y = y, + ang = ang, + vx = math.sin(ang) * rock_speed, + vy = -math.cos(ang) * rock_speed, + scale = size + } + setmetatable(rock, { __index = rock_meta }) + table.insert(rocks, rock) +end + +local function level_init() + for i=1,4 do + rock_create(math.random(30,width-30), math.random(30,height-30), math.random()*6.28, 30) + end + player.inv = 50 +end + +rock_meta = { + erase = function(self) + draw_rotated(poly_rock, self.x, self.y, self.ang, self.scale, bg) + end, + update = function(self, k) + self.x = (self.x + self.vx) % width + self.y = (self.y + self.vy) % height + + if player.inv == 0 and distance(self.x, self.y, player.x, player.y) < self.scale then + player:hit() + end + end, + hit = function(self, k, shot, sk) + sound.playPitch(1, 0.5, sound.drums.snare2) + self:erase() + score = score + ((rock_size + rock_step) - self.scale) + rock_speed = rock_speed + 0.06 + if self.scale > rock_step then + rock_create(self.x, self.y, shot.ang - math.random()*3.14, self.scale-rock_step) + rock_create(self.x, self.y, shot.ang + math.random()*3.14, self.scale-rock_step) + end + table.remove(rocks, k) + table.remove(shots, sk) + if #rocks == 0 then + rock_speed = rock_speed - 0.6 + score = score + 500 + level_init() + end + end, + draw = function(self) + draw_rotated(poly_rock, self.x, self.y, self.ang, self.scale, fg) + end +} + +function player:fire() + if self.cdown == 0 and #shots < max_shots then + sound.playPitch(1, 2, sound.drums.tom) + shot = { + x = self.x, + y = self.y, + ang = self.ang, + vx = math.sin(self.ang) * shot_speed, + vy = -math.cos(self.ang) * shot_speed, + } + setmetatable(shot, { __index = shot_meta }) + table.insert(shots, shot) + self.cdown = 8 + end +end + +function player:erase() + draw_rotated(poly_ship, self.x, self.y, self.ang, 1, bg) + draw_rotated(poly_thrust, self.x, self.y, self.ang, 1, bg) +end + +function player:update() + local angle_d = (keys.getState(keys.right) and 0.15 or 0) - (keys.getState(keys.left) and 0.15 or 0) + + self.ang = self.ang + angle_d + if keys.getState(keys.up) then + self.vx = (self.vx + math.sin(self.ang) * self.accel) + self.vy = (self.vy - math.cos(self.ang) * self.accel) + elseif keys.getState(keys.down) then + self.vx = (self.vx - sign(self.vx) * self.accel) + self.vy = (self.vy - sign(self.vy) * self.accel) + end + self.x = (self.x + self.vx) % width + self.y = (self.y + self.vy) % height + + if self.cdown > 0 then + self.cdown = self.cdown - 1 + else + if keys.getState(keys.enter) then + self:fire() + end + end + + if self.inv > 0 then self.inv = self.inv - 1 end +end + +function player:hit() + sound.playPitch(1, 0.4, sound.drums.snare1) + if lives == 0 then game_over = true return end + lives = lives - 1 + self.x = width/2 + self.y = height/2 + self.vx = 0 + self.vy = 0 + self.ang = 0 + self.inv = 50 +end + +function player:draw() + if math.floor(self.inv / 3) % 2 == 0 then + draw_rotated(poly_ship, self.x, self.y, self.ang, 1, fg) + end + if keys.getState(keys.up) then + draw_rotated(poly_thrust, self.x, self.y, self.ang, 1, fg) + end +end + +local function hud_draw() + draw.text(15,15," ",fg,bg) + draw.text(15,15,score,fg,bg) + for i = 0, 3 do + local color = i < lives and fg or bg + draw_rotated(poly_ship, width - 20 - 20*i, 20, 0, 1, color) + end +end + +term.clear() +--draw.enableBuffer(true) +draw.rectFill(0, 0, width, height, bg) +level_init() + +while true do + if keys.getState(keys.esc) or game_over then break end + + local now = os.clock() + if now >= last_frame + 0.05 then + last_frame = now + + -- erase + player:erase() + for _,v in pairs(shots) do v:erase() end + for _,v in pairs(rocks) do v:erase() end + + -- move player + player:update() + for k,v in pairs(shots) do v:update(k) end + for k,v in pairs(rocks) do v:update(k) end + + --draw.clear() + hud_draw() + player:draw() + for _,v in pairs(shots) do v:draw() end + for _,v in pairs(rocks) do v:draw() end + --draw.blitBuffer() + + collectgarbage() + end +end + +draw.enableBuffer(false) +if game_over then + term.clear() + print("game over!") + print("final score: "..score) +end diff --git a/Bin/PicoCalc SD/lua/boxworld.bmp b/Bin/PicoCalc SD/lua/boxworld.bmp new file mode 100644 index 0000000..4eb02ef Binary files /dev/null and b/Bin/PicoCalc SD/lua/boxworld.bmp differ diff --git a/Bin/PicoCalc SD/lua/boxworld.lua b/Bin/PicoCalc SD/lua/boxworld.lua new file mode 100644 index 0000000..c5532e8 --- /dev/null +++ b/Bin/PicoCalc SD/lua/boxworld.lua @@ -0,0 +1,448 @@ +-- BOXWORLD +-- original game and sprites by Jeng-Long Jiang, 1992 +-- picocalc lua conversion by maple "mavica" syrup, 2025 + +local sprite_size = 20 +local sprites = draw.loadBMPSprites("lua/boxworld.bmp", sprite_size, sprite_size) + +local fg,bg = colors.fromRGB(255,255,255),colors.fromRGB(0,0,0) +local screen = nil +local quit = false + +local function switch_screen(to) + screen = to + if screen.entry then screen.entry() end +end + +local music = { + tick = 0.10, + instrument = sound.instrument(sound.presets.square), + notes = { + c = 0, + C = 1, + d = 2, + D = 3, + e = 4, + f = 5, + F = 6, + g = 7, + G = 8, + a = 9, + A = 10, + b = 11 + }, + tracks = { + intro = { + "l2o3el1cl1el3dl1ccc", + "l3o1cga>cel2dl3ccl2c" + }, + victory = { + "l2o2el1Gl2al3bl1>dl2ee", + "l3o1aGel1gl2de" + } + }, + playing = nil +} +music.instrument.decay = 250 + +function music.perform() + if not music.playing then return end + local done_playing = true + for k,v in pairs(music.playing) do + local ch = music.channels[k] + local track = music.playing[k] + if ch.cursor <= #track then done_playing = false end + if ch.timer > 0 then ch.timer = ch.timer - 1 end + if ch.timer == 0 then + while ch.cursor <= #track do + local input = string.sub(track, ch.cursor, ch.cursor) + ch.cursor = ch.cursor + 1 + if input == 'l' then + input = string.sub(track, ch.cursor, ch.cursor) + ch.cursor = ch.cursor + 1 + ch.length = tonumber(input) + elseif input == 'o' then + input = string.sub(track, ch.cursor, ch.cursor) + ch.cursor = ch.cursor + 1 + ch.octave = tonumber(input) + elseif input == '<' then + ch.octave = ch.octave - 1 + elseif input == '>' then + ch.octave = ch.octave + 1 + elseif music.notes[input] then + ch.timer = ch.length + sound.play(k-1, music.notes[input] + (ch.octave+2) * 12, music.instrument) + break + end + end + end + end + if done_playing then music.playing = nil sys.stopTimer() end +end + +function music.play(track) + music.playing = track + music.channels = {} + for k,v in pairs(track) do + music.channels[k] = {} + music.channels[k].cursor = 1 + music.channels[k].length = 4 + music.channels[k].octave = 3 + music.channels[k].timer = 0 + end + sys.repeatTimer(music.tick * 1000, music.perform) +end + +local game = { + level = 1, + levelstrings = { + "00111\n00131\n00121111\n11142431\n13246111\n111141\n000131\n000111\n", + "11111\n16221\n124410111\n124210131\n111211131\n011222231\n012221221\n012221111\n011111\n", + "01111111\n0122222111\n1141112221\n1262422421\n1233124211\n113312221\n011111111\n", + "01111\n11221\n16421\n114211\n112421\n134221\n133531\n111111\n", + "011111\n0162111\n0124221\n11121211\n13121221\n13422121\n13222421\n11111111\n", + "0001111111\n1111222221\n1222311121\n12121222211\n12124241321\n12122522121\n12314242121\n1122221212111\n0121113222261\n0122222112221\n0111111111111\n", + "0001111111\n0011221261\n0012221221\n0014242421\n0012411221\n1112421211\n133333221\n111111111\n", + "000111111\n011122221\n1132411211\n1334242261\n1332424211\n111111221\n000001111\n", + "0111111111\n0122112221\n0122242221\n0142111241\n0121333121\n11213331211\n12422422421\n12222212621\n11111111111\n", + "00111111\n00122221\n11144421\n16243321\n12433311\n1111221\n0001111\n", + "011110011111\n112210012221\n124211114221\n122433332421\n112222126211\n01111111111\n", + "0011111\n1112261\n12243211\n12234321\n11125421\n00122211\n0011111\n", + "001111\n001331\n0112311\n0122431\n11242211\n12214421\n12262221\n11111111\n", + "11111111\n12212221\n12433421\n16435211\n12433421\n12212221\n11111111\n", + "0111111\n11222211\n12424421\n13333331\n12442421\n11126111\n001111\n", + "00111111\n0012222111\n0012422221\n1112421121\n1333242221\n1333414211\n1111212421\n0001226221\n0001111111\n", + "111111\n122221\n1244411\n122133111\n112233421\n012622221\n011111111\n", + "0011111111\n0012221321\n0112243331\n0122421531\n1121141211\n1222422421\n1222122221\n1111111621\n0000001111\n", + "01111111\n01333321\n1113334111\n1224142421\n1244221421\n1222212221\n1111262111\n00011111\n", + "1111111\n1334331\n1331331\n1244421\n1224221\n1244421\n1221621\n1111111\n", + "000111111\n000123331\n111133331\n12211142111\n12424224421\n16242422221\n12221112221\n11111011111\n", + "11111111\n12222221\n12144221\n12333121\n113334211\n012112421\n014224221\n012212261\n011111111\n", + "0011111\n1112221111\n1222424221\n1242224261\n1114411111\n00122331\n00133331\n00111111\n", + "11111100011111\n12222111012231\n12242421013331\n12122421112231\n12244422242631\n11122422412231\n00122414213331\n00112222212231\n00011111111111\n", + "00000111111\n01111132221\n01221331121\n01224332221\n01221231211\n11121141221\n12422224421\n12141221221\n16221111111\n11111\n", + "0111111111\n0122211221111\n0124222222221\n0114111211221\n0122112521211\n012433333321\n112111232121\n122222411141\n122212222461\n111114121111\n000012221\n000011111\n", + "000000111111111\n000000122222221\n000000121212121\n000000122424121\n111111122242221\n133122112424121\n133222112424221\n133122112111111\n133121242421\n133222224221\n122111262111\n1111011111\n", + "00001111\n11111221\n1224242101111111\n1222422101535351\n1124242111353531\n0142422122535351\n0164242222353511\n0142422122535351\n1124242111353531\n1222422101535351\n1224242101111111\n11111221\n00001111\n", + "11111111\n13333331\n122421211\n124212421\n114242421\n012262221\n011111111\n", + "001111111111\n111222322221\n122211411221\n126432323411\n11241141121\n01222232221\n01111111111\n", + "000111111\n111132261\n122444221\n131131131\n122242221\n122431211\n11112221\n00011111\n", + "0111111\n0132331\n0132431\n11122411\n12422421\n12141121\n12226221\n11111111\n", + "0000111111\n001112222111\n00122214222111\n00122242224421\n00124421422221\n00112224222421\n11111121411111\n1336214221\n1313322411\n133334121\n133332221\n111111111\n", + "111111111111111\n122222212222221\n124214212411421\n121224212222221\n122211414114421\n121212333212221\n124223212342121\n124164333121221\n122223212322421\n121134111432121\n121243333321121\n122222222222221\n111111111111111\n", + "111111111\n122211221\n121242421\n122531221\n112136311\n1141115111\n1222222221\n1222112121\n1111112221\n0000011111\n", + "11111111\n12222221\n1244222111\n12242444211111\n112112333222211\n012161333111421\n012124333222221\n112124333421211\n12211111211121\n12222224222421\n11111111111221\n00000000001111\n", + "00011111\n00012621\n00014441\n11112221\n122231411\n124343231\n122131311\n11111111\n", + "111111111111\n133321222221\n133221211221\n133222221221\n133221241121\n133321424221\n111111224421\n011224244221\n016244422121\n011242112221\n001222222221\n001111111111\n", + "111111111\n122222221\n122424241\n112141121\n0123323311\n0113323321\n00121141211\n00142424221\n00122222261\n00111111111\n", + "111110000001111\n162211111111221\n112422222224221\n012121221111221\n012242221111411\n01421121242421\n11242241222221\n12221222222121\n12221111141111\n1111122212221\n0000133322421\n0000133331221\n0000133331111\n0000111111\n", + "0000011111\n0111112221\n0123324121\n0121352221\n1125314211\n124224221\n122211261\n111111111\n", + "1111101111111\n1222111221221\n1242222242621\n1121411311221\n0122333532421\n0124121312121\n0112222422221\n0012211111111\n001111\n", + "000000000111\n0000111111611\n00001333314211\n00001333312421\n00001333324221\n00001233312221\n111111211111211\n124242224221221\n122224422242421\n111242424221111\n001122242421\n000122111111\n0001111\n", + "00011111\n0111222111\n1122642421\n12211211211\n12431342221\n12131512221\n12433322111\n111412111\n0012221\n0011111\n", + "0000001111\n000000122111111\n000000122221221\n000000124422221\n111111141221221\n122132332111411\n122131534222221\n122131351212221\n124433331211111\n12642121121\n12444122221\n12222111111\n111111\n", + "1111\n122111\n12422111\n1242422111\n124242422111\n124242422221\n1242422122211\n1242211244421\n16211112222211\n11210134444431\n012111333333311\n012223555555531\n011113333333331\n000011111111111\n", + "1111111\n12612211111\n12442242221\n12213114121\n11413332221\n11233311411\n12211311221\n12242242221\n12212221221\n11111111111\n", + "00011111\n00012621\n00012421\n00014341\n011134311\n11234343111\n12243434221\n12222322221\n11111111111\n", + "1111111111111\n1224242435331\n1242424253331\n1224242435331\n1242424253331\n1224242435331\n1242424253331\n1224242435331\n1242424253331\n1224242435331\n1642424253331\n1111111111111\n", + "00000000000001\n00000000000011\n00000000000111\n000000000012221\n000111111112121\n001242424242221\n01121313131641\n111333333322211\n01121212121411\n001242424242221\n000111111112121\n000000000012221\n00000000000111\n00000000000011\n", + "1111111\n122342111\n123434221\n154343621\n123434211\n12234221\n11111111\n", + "00000000011111\n00000000012221\n1111111111252111\n1222222222232221\n1244445555433361\n1222222222232221\n1111111111252111\n00000000012221\n00000000011111\n", + "000001111\n1111112211111\n1642222422421\n1411124212121\n1221221242221\n1241222212111\n122421412221\n133333333321\n111111112221\n000000011111\n", + "0111111111\n0122211221111\n0124222222221\n0114111211221\n0122113421211\n012433333321\n112111322121\n122222411141\n122212222461\n111114121111\n000012221\n000011111\n", + "11111111111\n12222122221\n12464444421\n12222222221\n11111211111\n00013221\n00013221\n00013331\n00013221\n00011111\n", + "0011111\n0012621\n0012421\n111232111\n122252221\n125555521\n122252221\n111454111\n0012321\n0012521\n0012321\n0011111\n", + "11111111111111\n13222222222221\n13424242424221\n13111111111221\n13135242334511\n1312424253461\n1313224233441\n1311111111131\n1322222222221\n1314141414141\n1322222222221\n1111111111111\n", + "111111111111\n13322122222111\n13322124224221\n13322141111221\n13322226211221\n13322121224211\n11111121142421\n00124224242421\n00122221222221\n00111111111111\n", + "00000001111\n11111111221111\n12221133333221\n12242211333121\n11224221112121\n01212422122221\n01221242212221\n01222124221221\n01222212421211\n0111122124221\n0000112212421\n0000011612221\n0000001111111\n", + "11110000001111\n13311111111331\n15353333353531\n12424242424241\n14242464242421\n12424242424241\n14242424242421\n13535333335351\n13311111111331\n11110000001111\n", + "000011111\n000112221111\n000123352421\n111121312221\n122223531611\n12141141121\n12222242421\n11221222111\n011111111\n", + "111111\n122221\n124221111\n124533521\n125335421\n111122421\n000126221\n000111111\n", + "00011111\n111132211\n124343221\n164121421\n124323221\n111141421\n001323221\n001111111\n", + "111111111111\n122223332421\n124445552461\n122223332421\n111111111111\n", + "1111111111\n1122222221\n1222141421\n1244223431\n1261113331\n1111111111\n", + "01111\n012211111\n114211221\n122464221\n122211421\n1113112111\n0133342421\n0113322221\n0011111111\n", + "1111101111\n1333101221111\n1333111224221\n133331124224111\n113333112224221\n111333211242421\n121122221224221\n1221121211121111\n1242121422422221\n1224262422224221\n1222124244242111\n12211111122111\n121100001111\n111\n", + "1111111\n1323231\n1244421\n1346431\n1244421\n1323231\n1111111\n", + "0000001111\n1111111221\n1222224221\n1222411641\n1141333121\n0124333221\n01213231211\n01222121421\n01422422221\n01221111111\n01111\n", + "000111111111\n000122212221\n000122222221\n111115111211\n12223332221\n121215111411\n124222242221\n111116212221\n000011111111\n", + "11111\n13331011111\n13331112221\n133332224411111\n1333322122122211\n1331411112141221\n1124221222224421\n1224126242441221\n1242424212224211\n122212242112221\n111111222111111\n0000011111\n", + "111111111111111\n111312222221111\n113312422421221\n133312112421221\n133333221442221\n113333422221421\n111121111111221\n122242222222211\n122421224124211\n124111242124421\n122261221122221\n111111111111111\n", + "000000011111111\n000000012212221\n011111112443331\n012222222213331\n012111111413331\n112122222213331\n122121424211111\n12124242421\n12622421221\n11111424421\n00001222221\n00001111111\n", + "000000011111\n001111112221\n111222232421\n124221431411\n122122631221\n112111132221\n012422151111\n01211213221\n01222223121\n01114222221\n00012211111\n0001111\n", + "00001111\n111112211111\n122224222421\n122414112221\n111213532111\n0012333261\n0011214111\n00012221\n00011111\n", + "00011111111\n11112222321\n12242424321\n12231111311\n1243424261\n1223221111\n1111111\n", + "0011111\n0012221\n11143411111\n12223242221\n12114112621\n12223211111\n1112321\n0012221\n0011111\n", + "011111\n01262111111\n01213352221\n01233312221\n11411242421\n12221411111\n122242221\n111112121\n000012221\n000011111\n", + "00000111111\n000111222211\n000122211221\n011141122121\n112222233121\n122414153121\n12446213512111\n12244213312221\n11222213342221\n01114113212111\n000122111221\n000112222211\n00001111111\n", + "000001111111\n000001221221\n000001224421\n111111241221\n1333111212211\n1322122421221\n1322224242421\n1322122421221\n1333111212211\n111111242221\n000001621221\n000001111111\n", + "0000000000111111\n0000000000122221\n1111100011121121\n1332111112422121\n1332222242224121\n1332211211222121\n1332112421424121\n1332122222422121\n1332122421114221\n1332124242242111\n111211212422221\n001222216112421\n001111111112211\n00000000001111\n", + "00000001\n0000011111\n000111262111\n000122424221\n000125353521\n0011234243211\n01112535352111\n111122424221111\n000111111221\n00001100011\n", + "011111111111111\n0126252525212211\n0141225252212221\n0121252525222221\n0121225252211211\n012125252521121\n012122525221121\n012125252521121\n012122525221121\n0121252325211211\n1121111111111221\n1222222222222221\n1222111111111221\n1111100000001111\n", + "01111111111111\n01222212211221\n014442124422411\n012422122333321\n012242214311321\n012212412333311\n114242214311321\n124224264333321\n122211122111111\n1111101111\n", + "00000111111111\n00000122222221\n00000122414121\n01111112212421\n01222124224221\n11242222211121\n12221411112221\n12222421112111\n1111133261211\n0001333424421\n0001333122221\n0001333111111\n00011111\n", + "1111000001111\n122111111133111\n124242422133331\n124222442155531\n124242422133531\n122424242153531\n1124242423535311\n1224242423535361\n1224242421535311\n124242422133531\n124222442155531\n124242422133331\n122111111133111\n1111000001111\n", + "00000011111\n11111112221\n12221132221\n122413322111\n1122333144211111\n0124313422222221\n0124111411212421\n0122212222244121\n0114412114142221\n0133324624222111\n01333141222111\n013332211111\n01111111\n", + "0111100111111\n0122111122221\n1152225255221\n1242522225121\n1232221112221\n1111112221611\n1252325225521\n1222122212221\n1152225214121\n0122111112221\n0111100011111\n", + "0111111111\n0122212221\n0124444421\n1124242421\n1242262221\n12421111211\n12213333321\n11223333321\n01111111111\n", + "0001111111\n0001222221\n00012424211\n000111113311111\n111111335322421\n122464333314421\n122242141112221\n111112222222111\n0000111221111\n0000001221\n0000001111\n", + "11111\n1222111111\n1242332421\n1142334461\n0122332421\n0111111111\n", + "0011111\n001222111111\n111413222221\n124233312421\n162431542221\n111122221111\n000111111\n", + "000011111\n111112221\n122242621\n122421311111\n114211311221111\n012233333241221\n012411311221421\n012221311222221\n011124211111211\n000121422222421\n000122221112221\n000111111011111\n", + "1111111\n1225221\n1263421\n1243221\n1535551\n1225221\n1245421\n1223221\n1111111\n", + "00111111111\n00122221221\n11121422421111\n1224221133122111\n1212242133424221\n1244224133221221\n1122122333142221\n0142621333124221\n0122221333422211\n012114211122211\n01222422222111\n012211111111\n01111\n", + "0001111111111\n1111333333221\n1222333331221\n1221333333211\n112111141141\n16422424222111\n12442222112221\n12122441122121\n12224221244221\n12242242221421\n11112221242421\n00012221222221\n00011111111111\n", + "000000111111111\n000000122222221\n000000142444221\n000000122212421\n000000122246421\n000011142421211\n00001222414121\n11111212212221\n13332212412111\n133333222121\n133333124121\n111111112221\n000000011111\n", + "0111110111111\n0122211122221\n1124242142141\n12242624224211\n12122112133331\n12211242131131\n11224222233331\n01244214133331\n01222122214211\n0111112422221\n0000011112211\n000000001111\n", + "1111\n1221\n1221111111111\n1222211222221\n1331222244121\n133221122242111\n133122114124221\n133222126424221\n133122124242221\n123222124242111\n1221221222111\n12212222111\n111111111\n", + "00000000011111\n000000001222221\n0000000122411221\n0000001221222421\n1001001212212121\n0101001242422421\n0011111112141221\n001224242222221\n00163342553111\n000133333311111\n0000111111111111\n" + }, + anim_timer = 0, + player_flip = 0, + player_sprites = { + U = {7, 8}, + D = {5, 6}, + L = {11, 12}, + R = {9, 10}, + W = {13, 13} + } +} +game.push_sound = sound.instrument(sound.drums.tom) +game.push_sound.volume = 0.5 +game.push_sound.decay = 150 +game.push_sound.sustain = 0 + +local title = {} + +function title.input() + local state, _, code = keys.poll() + + if state == keys.states.pressed then + if code == keys.left then + game.level = ((game.level - 2) % #game.levelstrings) + 1 + screen.redraw = true + elseif code == keys.right then + game.level = ((game.level) % #game.levelstrings) + 1 + screen.redraw = true + elseif code == keys.enter then + switch_screen(game) + elseif code == keys.esc then + quit = true + end + end +end + +function title.entry() + draw.enableBuffer(false) + music.play(music.tracks.intro) + draw.clear() + screen.redraw = true +end + +function title.draw() + -- boxworld logo + for j = 0, 1 do + for i = 0, 6 do + sprites:blit(160 - 70 + i * sprite_size, 80 + j * sprite_size, 14 + i + j * 7) + end + end + + draw.text(160, 140, "Original by Jeng-Long Jiang, 1992",fg,bg,draw.align_center) + draw.text(160, 160, "Reprogram by maple \"mavica\" syrup, 2025",fg,bg,draw.align_center) + draw.text(160, 180, "Select level",fg,bg,draw.align_center) + draw.text(160, 190, " < " .. game.level .. " > ",fg,bg,draw.align_center) +end + +function game.entry() + -- load level + game.leveltable = {} + local row = {} + local longest = 0 + local str = game.levelstrings[game.level] + for i = 1, #str do + if str:sub(i,i) == '\n' then + table.insert(game.leveltable, row) + if #row > longest then longest = #row end + row = {} + elseif str:sub(i,i) == '6' then -- player + game.player_pos = {#row + 1, #game.leveltable + 1} + game.player_direction = "D" + table.insert(row, '2') -- empty floor under player + else + table.insert(row, str:sub(i,i)) + end + end + game.draw_offset = {140 - longest/2*sprite_size, 140 - #game.leveltable/2*sprite_size} + game.moves = {} + collectgarbage() + + draw.enableBuffer(1) + draw.clear() + screen.redraw = true +end + +function game.draw() + for j = 1, #game.leveltable do + local row = game.leveltable[j] + for i = 1, #row do + local pos = {game.draw_offset[1] + i * sprite_size, game.draw_offset[2] + j * sprite_size} + if row[i] == "1" then -- wall + sprites:blit(pos[1], pos[2], 0) + elseif row[i] == "2" then -- floor + sprites:blit(pos[1], pos[2], 1) + elseif row[i] == "3" then -- floor with goal + sprites:blit(pos[1], pos[2], 1) + sprites:blit(pos[1], pos[2], 4) + elseif row[i] == "4" then -- box + sprites:blit(pos[1], pos[2], 2) + elseif row[i] == "5" then -- box on goal + sprites:blit(pos[1], pos[2], 3) + end + end + end + + local pos = { + game.draw_offset[1] + game.player_pos[1] * sprite_size, + game.draw_offset[2] + game.player_pos[2] * sprite_size + } + sprites:blit(pos[1], pos[2], game.player_sprites[game.player_direction][game.player_flip+1]) + + if game.player_direction == "W" then + draw.text(160, 160, "You win !!!",fg,bg,draw.align_center) + draw.text(160, 170, "Press ENTER for next level",fg,bg,draw.align_center) + end + + draw.text(0, 300, "Box World! #" .. game.level .. " - Moves: " .. #game.moves .. " ") + draw.text(0, 310, "(U)ndo, (R)estart, (ESC) Quit") + + draw.blitBuffer() +end + +function game.check_win() + for j = 1, #game.leveltable do + local row = game.leveltable[j] + for i = 1, #row do + if row[i] == "4" then -- box not on goal + return false -- quit as soon as found any + end + end + end + -- didn't quit? must mean we won + game.player_direction = "W" + music.play(music.tracks.victory) +end + +function game.process_push(code) + if game.player_direction == "W" then return end -- don't move if we've already won + local ahead, ahead2 + if code == keys.up then + game.player_direction = "U" + ahead = {game.player_pos[1], game.player_pos[2]-1} + ahead2 = {game.player_pos[1], game.player_pos[2]-2} + elseif code == keys.down then + game.player_direction = "D" + ahead = {game.player_pos[1], game.player_pos[2]+1} + ahead2 = {game.player_pos[1], game.player_pos[2]+2} + elseif code == keys.left then + game.player_direction = "L" + ahead = {game.player_pos[1]-1, game.player_pos[2]} + ahead2 = {game.player_pos[1]-2, game.player_pos[2]} + elseif code == keys.right then + game.player_direction = "R" + ahead = {game.player_pos[1]+1, game.player_pos[2]} + ahead2 = {game.player_pos[1]+2, game.player_pos[2]} + end + + local whats_ahead = game.leveltable[ahead[2]] + if whats_ahead ~= nil then whats_ahead = whats_ahead[ahead[1]] end + local whats_ahead2 = game.leveltable[ahead2[2]] + if whats_ahead2 ~= nil then whats_ahead2 = whats_ahead2[ahead2[1]] end + + if whats_ahead == '2' or whats_ahead == '3' then -- ahead is floor or empty goal + game.player_pos = ahead + table.insert(game.moves, string.lower(game.player_direction)) + elseif whats_ahead == '4' or whats_ahead == '5' then -- ahead is box, whether or not on a goal + if whats_ahead2 == '2' or whats_ahead2 == '3' then -- box pushed onto floor or goal + sound.playPitch(2,1,game.push_sound) + local box_leaves = whats_ahead == '4' and '2' or '3' + local box_becomes = whats_ahead2 == '2' and '4' or '5' + game.player_pos = ahead + game.leveltable[ahead[2]][ahead[1]] = box_leaves + game.leveltable[ahead2[2]][ahead2[1]] = box_becomes + table.insert(game.moves, game.player_direction) + game.check_win() + end + end + screen.redraw = true +end + +function game.process_undo() + if #game.moves < 1 then return false end + + local return_to, pushed + local move = table.remove(game.moves) + game.player_direction = string.upper(move) + + if string.upper(move) == 'D' then + return_to = {game.player_pos[1], game.player_pos[2]-1} + pushed = {game.player_pos[1], game.player_pos[2]+1} + elseif string.upper(move) == 'U' then + return_to = {game.player_pos[1], game.player_pos[2]+1} + pushed = {game.player_pos[1], game.player_pos[2]-1} + elseif string.upper(move) == 'R' then + return_to = {game.player_pos[1]-1, game.player_pos[2]} + pushed = {game.player_pos[1]+1, game.player_pos[2]} + elseif string.upper(move) == 'L' then + return_to = {game.player_pos[1]+1, game.player_pos[2]} + pushed = {game.player_pos[1]-1, game.player_pos[2]} + end + + if move == string.upper(move) then -- if move was recorded upper means we moved a box + local what_box_became = game.leveltable[pushed[2]][pushed[1]] + local box_leaves = what_box_became == '4' and '2' or '3' + local box_becomes = game.leveltable[game.player_pos[2]][game.player_pos[1]] == '2' and '4' or '5' + game.leveltable[pushed[2]][pushed[1]] = box_leaves + game.leveltable[game.player_pos[2]][game.player_pos[1]] = box_becomes + end + game.player_pos = return_to + + screen.redraw = true +end + +function game.input() + local state, _, code = keys.poll() + + if state == keys.states.pressed then + if code == keys.up or code == keys.down or code == keys.left or code == keys.right then + game.process_push(code) + elseif code == 'u' then + game.process_undo() + elseif code == 'r' then + game.entry() + elseif code == keys.esc then + switch_screen(title) + elseif game.player_direction == "W" and code == keys.enter then + -- go to next level when won and pressed enter + game.level = ((game.level) % #game.levelstrings) + 1 + game.entry() + end + end + collectgarbage() +end + +function game.update(now) + if game.player_direction ~= 'W' and now > game.anim_timer then + game.player_flip = (game.player_flip + 1) % 2 + game.anim_timer = now + 1 + screen.redraw = true + end +end + +switch_screen(title) +while true do + now = os.clock() + if screen.input then screen.input() end + if quit then break end + if screen.update then screen.update(now) end + if screen.redraw and screen.draw then screen.draw() screen.redraw = false end +end diff --git a/Bin/PicoCalc SD/lua/browser.lua b/Bin/PicoCalc SD/lua/browser.lua new file mode 100644 index 0000000..51743b3 --- /dev/null +++ b/Bin/PicoCalc SD/lua/browser.lua @@ -0,0 +1,74 @@ +local current_dir = "/" +local cursor_y = 1 +local width, height = term.getSize() + +function string:endswith(suffix) + return self:sub(-#suffix) == suffix +end + +term.clear() +while true do + local dir = fs.list(current_dir) + term.setCursorPos(1,1) + + table.sort(dir, function(a, b) return string.upper(a.name) < string.upper(b.name) end) + local folders = {} + local files = {} + for _,v in pairs(dir) do + if v.isDir then table.insert(folders, v) + else table.insert(files, v) end + end + table.sort(files, function(a, b) return a.name < b.name end) + dir = folders + for _,v in ipairs(files) do + table.insert(dir, v) + end + + if current_dir > "/" then + table.insert(dir, 1, {name="..", isDir=true}) + end + + local off = 0 + if #dir > height-2 then + off = math.floor(cursor_y / #dir * (#dir - height + 3)) + end + + for y = 1, height - 2 do + local yoff = y + off + local v = dir[yoff] + if v then + local str = "" + if v.isDir then str = str .. "\27[93m" + elseif v.name:endswith(".lua") then str = str .. "\27[96m" + else str = str .. "\27[97m" end + if yoff == cursor_y then str = str .. "\27[7m" end + term.write(str .. v.name .. "\27[m\27[K\n") + else + term.write("\27[K\n") + end + end + + term.write("\n\27[93mCurrent directory: " .. current_dir .. "\27[m\27[K") + + local state, _, code = keys.wait(true, true) + + if code == keys.up then + cursor_y = ((cursor_y - 2) % #dir) + 1 + elseif code == keys.down then + cursor_y = ((cursor_y) % #dir) + 1 + elseif code == keys.enter then + if dir[cursor_y].isDir then + if dir[cursor_y].name == ".." then + current_dir = current_dir:match("(.*/).*/") or '/' + else + current_dir = current_dir .. dir[cursor_y].name .. '/' + end + cursor_y = 1 + elseif dir[cursor_y].name:endswith(".lua") then + edit(current_dir .. dir[cursor_y].name) + end + elseif code == keys.esc then + break + end +end +term.clear() \ No newline at end of file diff --git a/Bin/PicoCalc SD/lua/bubble.lua b/Bin/PicoCalc SD/lua/bubble.lua new file mode 100644 index 0000000..0615548 --- /dev/null +++ b/Bin/PicoCalc SD/lua/bubble.lua @@ -0,0 +1,50 @@ +-- Bubble Universe +-- inspired by https://forum.clockworkpi.com/t/bubble-universe/19907 + +local w = 320 +local w2 = w/2 +local rad = 70 +local n = 20 +local nc = 120 +local step = 2 +local r = math.pi*2/n +local x,y,t = 0,0,6 +local u,v,px,py + +local res = 100 + +local sint = {} +for i = 1, math.floor(math.pi*2*res) do + sint[i] = math.sin(i/res) +end + +local function fsin(i) + return sint[(math.floor(i*res)%#sint)+1] +end + +local function fcos(i) + return sint[((math.floor((math.pi/2-i)*res))%#sint)+1] +end + +draw.enableBuffer(2) +while true do + if keys.getState(keys.esc) then break end + draw.clear() + for i = 0, n-1 do + for c = 0, nc-1, step do + u = fsin(i+y)+fsin(r*i+x) + v = fcos(i+y)+fcos(r*i+x) + x = u + t + y = v + px = u * rad + w2 + py = y * rad + w2 + draw.point(px, py, + --draw.rectFill(px, py, 2, 2, + colors.fromRGB(math.floor(63+i/n*192), + math.floor(63+c/nc*192),168)) + end + end + t=t+0.02 + draw.blitBuffer() +end +draw.enableBuffer(false) diff --git a/Bin/PicoCalc SD/lua/mandelbrot.lua b/Bin/PicoCalc SD/lua/mandelbrot.lua new file mode 100644 index 0000000..ec429c2 --- /dev/null +++ b/Bin/PicoCalc SD/lua/mandelbrot.lua @@ -0,0 +1,107 @@ +-- partially adapted from https://bisqwit.iki.fi/jutut/kuvat/programming_examples/mandelbrotbtrace.pdf +-- arrow keys, 9 and 0 to control view, escape to quit + +local maxIter = 64 +local chunk = 8 +local center = {-1,0} +local radius = 1 +local minX, maxX, minY, maxY +local wid, hei = 320, 320 + +local function iterate(zr, zi, max) + local cnt, r, i, r2, i2, ri + cnt = 0 + r,i = zr,zi + while cnt < max do + r2 = r*r; i2 = i*i + if r2+i2 >= 4 then break end + ri = r*i + i = ri+ri + zi + r = r2-i2 + zr + cnt = cnt + 1 + end + return cnt +end + +local function is_control_key() + local state, _, code = keys.peek() + if state == keys.states.pressed then + if code == keys.up + or code == keys.down + or code == keys.left + or code == keys.right + or code == '9' + or code == '0' + or code == '[' + or code == ']' + or code == keys.esc then + return true + end + end + keys.flush() + return false +end + +local function drawScanlineMandelbrot(max) + local zr, zi, cnt, clr + local st = chunk + local proc = {} + local stepR = (maxX - minX) / wid + local stepI = (maxY - minY) / hei + while st >= 1 do + for y = 0, hei - 1, st do + zi = minY + y * stepI + if proc[(y % (st*2))] then goto next end + for x = 0, wid - 1 do + zr = minX + x * stepR + cnt = iterate(zr, zi, max) + if cnt == max then clr = 0 + else + cnt = math.floor(cnt/max*255) + clr = colors.fromHSV((-cnt-32)%256,255,127+math.floor(cnt/2)) + end + draw.line(x, y, x, y+st-1, clr) + if is_control_key() then return end + end + ::next:: + end + proc[st%chunk] = true + st = math.floor(st/2) + end +end + +while true do + minX, maxX, minY, maxY = center[1]-radius, center[1]+radius, center[2]-radius, center[2]+radius + drawScanlineMandelbrot(maxIter) + while true do + local _, _, code = keys.wait(false, true) + if code == keys.up then + center[2] = center[2] - radius * 0.25 + break + elseif code == keys.down then + center[2] = center[2] + radius * 0.25 + break + elseif code == keys.left then + center[1] = center[1] - radius * 0.25 + break + elseif code == keys.right then + center[1] = center[1] + radius * 0.25 + break + elseif code == '9' then + radius = radius * 4 + break + elseif code == '0' then + radius = radius * 0.25 + break + elseif code == '[' then + maxIter = maxIter - 10 + break + elseif code == ']' then + maxIter = maxIter + 10 + break + elseif code == keys.esc then + goto exit + end + end +end +::exit:: diff --git a/Bin/PicoCalc SD/lua/piano.lua b/Bin/PicoCalc SD/lua/piano.lua new file mode 100644 index 0000000..c962782 --- /dev/null +++ b/Bin/PicoCalc SD/lua/piano.lua @@ -0,0 +1,84 @@ +-- kalimba / piano demo +-- written by maple "mavica" syrup + +local pkeys = {} + +local keymap = { + "5","6","4","7","3","8","2","9", + "t","y","r","u","e","i","w","o", + "g","h","f","j","d","k","s","l", + "b","n","v","m","c",",","x","." +} + +local qwertymap = { + "2","3","4","5","6","7","8","9", + "w","e","r","t","y","u","i","o", + "s","d","f","g","h","j","k","l", + "x","c","v","b","n","m",",","." +} + +for k,v in pairs(keymap) do + pkeys[v] = k - 1 +end + +local last = nil +local ch = 0 +local octave = 3 + +local inst_num = 0 +local insts = {} +local inst_names = {} + +for k,v in pairs(sound.presets) do + table.insert(insts, v) + table.insert(inst_names, k) +end + +local function noteString(note) + local notes = {"C-", "C#", "D-", "D#", "E-", "F-", "F#", "G-", "G#", "A-", "A#", "B-"} + return notes[(note % 12)+1] .. math.floor(note / 12) +end + +local function redraw() + term.setCursorPos(1,1) + term.write("Piano / Kalimba demo\n\n") + for j = 0, 24, 8 do + for i = 1, 8 do + if qwertymap[i+j] == last then term.write("\27[7m") end + term.write(qwertymap[i+j].."\27[m ") + end + term.write("\n") + end + term.write("\n") + if pkeys[last] then term.write(noteString(pkeys[last] + octave * 12)) end + term.write("\n\n") + term.write("Octave: " .. octave .. " | Instrument: " .. inst_names[inst_num+1] .. "\n\x1b[K") + term.write("/ \\ - Change octave\n[ ] - Change instrument\nEsc - Quit\n") +end + +term.clear() +while true do + redraw() + + local state, _, code = keys.wait(false, false) + + if state == keys.states.pressed and code ~= last then + if pkeys[code] then + last = code + sound.play(ch, pkeys[code] + octave * 12, insts[inst_num+1]) + ch = (ch+1) % 4 + elseif code == "[" then + inst_num = (inst_num - 1) % #insts + elseif code == "]" then + inst_num = (inst_num + 1) % #insts + elseif code == "/" and octave > 0 then + octave = (octave - 1) + elseif code == "\\" and octave < 6 then + octave = (octave + 1) + elseif code == keys.esc then + break + end + elseif state == keys.states.released then + last = nil + end +end diff --git a/Bin/PicoCalc SD/main.lua b/Bin/PicoCalc SD/main.lua new file mode 100644 index 0000000..f2ff2ce --- /dev/null +++ b/Bin/PicoCalc SD/main.lua @@ -0,0 +1,4 @@ +--ccedit = loadfile("cc/edit.lua") +--if ccedit then print("Editor loaded") end +browser = loadfile("lua/browser.lua") +if browser then print("File browser available at: browser()") end \ No newline at end of file diff --git a/Bin/PicoCalc SD/pico1-apps/MicroPython_fa8b24c.uf2 b/Bin/PicoCalc SD/pico1-apps/MicroPython_fa8b24c.uf2 new file mode 100644 index 0000000..33f1dc8 Binary files /dev/null and b/Bin/PicoCalc SD/pico1-apps/MicroPython_fa8b24c.uf2 differ diff --git a/Bin/PicoCalc SD/firmware/PicoCalc_Fuzix_v1.0.uf2 b/Bin/PicoCalc SD/pico1-apps/PicoCalc_Fuzix_v1.0.uf2 similarity index 100% rename from Bin/PicoCalc SD/firmware/PicoCalc_Fuzix_v1.0.uf2 rename to Bin/PicoCalc SD/pico1-apps/PicoCalc_Fuzix_v1.0.uf2 diff --git a/Bin/PicoCalc SD/firmware/PicoCalc_MP3Player_v0.5.uf2 b/Bin/PicoCalc SD/pico1-apps/PicoCalc_MP3Player_v0.5.uf2 similarity index 100% rename from Bin/PicoCalc SD/firmware/PicoCalc_MP3Player_v0.5.uf2 rename to Bin/PicoCalc SD/pico1-apps/PicoCalc_MP3Player_v0.5.uf2 diff --git a/Bin/PicoCalc SD/firmware/PicoCalc_NES_v1.0.uf2 b/Bin/PicoCalc SD/pico1-apps/PicoCalc_NES_v1.0.uf2 similarity index 100% rename from Bin/PicoCalc SD/firmware/PicoCalc_NES_v1.0.uf2 rename to Bin/PicoCalc SD/pico1-apps/PicoCalc_NES_v1.0.uf2 diff --git a/Bin/PicoCalc SD/firmware/PicoCalc_uLisp_v1.1.uf2 b/Bin/PicoCalc SD/pico1-apps/PicoCalc_uLisp_v1.1.uf2 similarity index 100% rename from Bin/PicoCalc SD/firmware/PicoCalc_uLisp_v1.1.uf2 rename to Bin/PicoCalc SD/pico1-apps/PicoCalc_uLisp_v1.1.uf2 diff --git a/Bin/PicoCalc SD/pico1-apps/PicoMite_v6.02.01b4_beta.uf2 b/Bin/PicoCalc SD/pico1-apps/PicoMite_v6.02.01b4_beta.uf2 new file mode 100644 index 0000000..f7998be Binary files /dev/null and b/Bin/PicoCalc SD/pico1-apps/PicoMite_v6.02.01b4_beta.uf2 differ diff --git a/Bin/PicoCalc SD/pico1-apps/Picoware_v1.6.9.uf2 b/Bin/PicoCalc SD/pico1-apps/Picoware_v1.6.9.uf2 new file mode 100644 index 0000000..f5dc45a Binary files /dev/null and b/Bin/PicoCalc SD/pico1-apps/Picoware_v1.6.9.uf2 differ diff --git a/Bin/PicoCalc SD/pico1-apps/phyllosoma_kb.uf2 b/Bin/PicoCalc SD/pico1-apps/phyllosoma_kb.uf2 new file mode 100644 index 0000000..faa0016 Binary files /dev/null and b/Bin/PicoCalc SD/pico1-apps/phyllosoma_kb.uf2 differ diff --git a/Bin/PicoCalc SD/pico1-apps/picolua_daf20a2.uf2 b/Bin/PicoCalc SD/pico1-apps/picolua_daf20a2.uf2 new file mode 100644 index 0000000..5820306 Binary files /dev/null and b/Bin/PicoCalc SD/pico1-apps/picolua_daf20a2.uf2 differ diff --git a/Bin/PicoCalc SD/picoware/apps/Calculator.mpy b/Bin/PicoCalc SD/picoware/apps/Calculator.mpy new file mode 100644 index 0000000..53cabfd Binary files /dev/null and b/Bin/PicoCalc SD/picoware/apps/Calculator.mpy differ diff --git a/Bin/PicoCalc SD/picoware/apps/FlipSocial.mpy b/Bin/PicoCalc SD/picoware/apps/FlipSocial.mpy new file mode 100644 index 0000000..9d2fd7e Binary files /dev/null and b/Bin/PicoCalc SD/picoware/apps/FlipSocial.mpy differ diff --git a/Bin/PicoCalc SD/picoware/apps/Graph.mpy b/Bin/PicoCalc SD/picoware/apps/Graph.mpy new file mode 100644 index 0000000..6f07d60 Binary files /dev/null and b/Bin/PicoCalc SD/picoware/apps/Graph.mpy differ diff --git a/Bin/PicoCalc SD/picoware/apps/Serial Terminal.mpy b/Bin/PicoCalc SD/picoware/apps/Serial Terminal.mpy new file mode 100644 index 0000000..7f9baa3 Binary files /dev/null and b/Bin/PicoCalc SD/picoware/apps/Serial Terminal.mpy differ diff --git a/Bin/PicoCalc SD/picoware/apps/Text Editor.mpy b/Bin/PicoCalc SD/picoware/apps/Text Editor.mpy new file mode 100644 index 0000000..721cc26 Binary files /dev/null and b/Bin/PicoCalc SD/picoware/apps/Text Editor.mpy differ diff --git a/Bin/PicoCalc SD/picoware/apps/Weather.mpy b/Bin/PicoCalc SD/picoware/apps/Weather.mpy new file mode 100644 index 0000000..b15836b Binary files /dev/null and b/Bin/PicoCalc SD/picoware/apps/Weather.mpy differ diff --git a/Bin/PicoCalc SD/picoware/apps/cat-fact.mpy b/Bin/PicoCalc SD/picoware/apps/cat-fact.mpy new file mode 100644 index 0000000..62acd55 Binary files /dev/null and b/Bin/PicoCalc SD/picoware/apps/cat-fact.mpy differ diff --git a/Bin/PicoCalc SD/picoware/apps/counter.mpy b/Bin/PicoCalc SD/picoware/apps/counter.mpy new file mode 100644 index 0000000..a693a05 Binary files /dev/null and b/Bin/PicoCalc SD/picoware/apps/counter.mpy differ diff --git a/Bin/PicoCalc SD/picoware/apps/flip_social/__init__.py b/Bin/PicoCalc SD/picoware/apps/flip_social/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Bin/PicoCalc SD/picoware/apps/flip_social/password.mpy b/Bin/PicoCalc SD/picoware/apps/flip_social/password.mpy new file mode 100644 index 0000000..b0c55f5 Binary files /dev/null and b/Bin/PicoCalc SD/picoware/apps/flip_social/password.mpy differ diff --git a/Bin/PicoCalc SD/picoware/apps/flip_social/run.mpy b/Bin/PicoCalc SD/picoware/apps/flip_social/run.mpy new file mode 100644 index 0000000..4808c31 Binary files /dev/null and b/Bin/PicoCalc SD/picoware/apps/flip_social/run.mpy differ diff --git a/Bin/PicoCalc SD/picoware/apps/flip_social/settings.mpy b/Bin/PicoCalc SD/picoware/apps/flip_social/settings.mpy new file mode 100644 index 0000000..d3e6ecb Binary files /dev/null and b/Bin/PicoCalc SD/picoware/apps/flip_social/settings.mpy differ diff --git a/Bin/PicoCalc SD/picoware/apps/flip_social/username.mpy b/Bin/PicoCalc SD/picoware/apps/flip_social/username.mpy new file mode 100644 index 0000000..7904d24 Binary files /dev/null and b/Bin/PicoCalc SD/picoware/apps/flip_social/username.mpy differ diff --git a/Bin/PicoCalc SD/picoware/apps/games/2048.mpy b/Bin/PicoCalc SD/picoware/apps/games/2048.mpy new file mode 100644 index 0000000..eda239a Binary files /dev/null and b/Bin/PicoCalc SD/picoware/apps/games/2048.mpy differ diff --git a/Bin/PicoCalc SD/picoware/apps/games/Breakout.mpy b/Bin/PicoCalc SD/picoware/apps/games/Breakout.mpy new file mode 100644 index 0000000..2913d66 Binary files /dev/null and b/Bin/PicoCalc SD/picoware/apps/games/Breakout.mpy differ diff --git a/Bin/PicoCalc SD/picoware/apps/games/Flappy Bird.mpy b/Bin/PicoCalc SD/picoware/apps/games/Flappy Bird.mpy new file mode 100644 index 0000000..53f464b Binary files /dev/null and b/Bin/PicoCalc SD/picoware/apps/games/Flappy Bird.mpy differ diff --git a/Bin/PicoCalc SD/picoware/apps/games/FlipWorld.mpy b/Bin/PicoCalc SD/picoware/apps/games/FlipWorld.mpy new file mode 100644 index 0000000..03a63c0 Binary files /dev/null and b/Bin/PicoCalc SD/picoware/apps/games/FlipWorld.mpy differ diff --git a/Bin/PicoCalc SD/picoware/apps/games/Free Roam.mpy b/Bin/PicoCalc SD/picoware/apps/games/Free Roam.mpy new file mode 100644 index 0000000..06b45e9 Binary files /dev/null and b/Bin/PicoCalc SD/picoware/apps/games/Free Roam.mpy differ diff --git a/Bin/PicoCalc SD/picoware/apps/games/Maze Runner.mpy b/Bin/PicoCalc SD/picoware/apps/games/Maze Runner.mpy new file mode 100644 index 0000000..1b7c134 Binary files /dev/null and b/Bin/PicoCalc SD/picoware/apps/games/Maze Runner.mpy differ diff --git a/Bin/PicoCalc SD/picoware/apps/games/Minesweeper.mpy b/Bin/PicoCalc SD/picoware/apps/games/Minesweeper.mpy new file mode 100644 index 0000000..6e47a1e Binary files /dev/null and b/Bin/PicoCalc SD/picoware/apps/games/Minesweeper.mpy differ diff --git a/Bin/PicoCalc SD/picoware/apps/games/Pong.mpy b/Bin/PicoCalc SD/picoware/apps/games/Pong.mpy new file mode 100644 index 0000000..141eb28 Binary files /dev/null and b/Bin/PicoCalc SD/picoware/apps/games/Pong.mpy differ diff --git a/Bin/PicoCalc SD/picoware/apps/games/Snake.mpy b/Bin/PicoCalc SD/picoware/apps/games/Snake.mpy new file mode 100644 index 0000000..6e33889 Binary files /dev/null and b/Bin/PicoCalc SD/picoware/apps/games/Snake.mpy differ diff --git a/Bin/PicoCalc SD/picoware/apps/games/Soduko.mpy b/Bin/PicoCalc SD/picoware/apps/games/Soduko.mpy new file mode 100644 index 0000000..19ad168 Binary files /dev/null and b/Bin/PicoCalc SD/picoware/apps/games/Soduko.mpy differ diff --git a/Bin/PicoCalc SD/picoware/apps/games/Space Invaders.mpy b/Bin/PicoCalc SD/picoware/apps/games/Space Invaders.mpy new file mode 100644 index 0000000..a5d7892 Binary files /dev/null and b/Bin/PicoCalc SD/picoware/apps/games/Space Invaders.mpy differ diff --git a/Bin/PicoCalc SD/picoware/apps/games/Tetris.mpy b/Bin/PicoCalc SD/picoware/apps/games/Tetris.mpy new file mode 100644 index 0000000..6c15177 Binary files /dev/null and b/Bin/PicoCalc SD/picoware/apps/games/Tetris.mpy differ diff --git a/Bin/PicoCalc SD/picoware/apps/games/Tower Defense.mpy b/Bin/PicoCalc SD/picoware/apps/games/Tower Defense.mpy new file mode 100644 index 0000000..948a346 Binary files /dev/null and b/Bin/PicoCalc SD/picoware/apps/games/Tower Defense.mpy differ diff --git a/Bin/PicoCalc SD/picoware/apps/games/example.mpy b/Bin/PicoCalc SD/picoware/apps/games/example.mpy new file mode 100644 index 0000000..ca418a0 Binary files /dev/null and b/Bin/PicoCalc SD/picoware/apps/games/example.mpy differ diff --git a/Bin/PicoCalc SD/picoware/apps/games/flip_world/__init__.py b/Bin/PicoCalc SD/picoware/apps/games/flip_world/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Bin/PicoCalc SD/picoware/apps/games/flip_world/assets.mpy b/Bin/PicoCalc SD/picoware/apps/games/flip_world/assets.mpy new file mode 100644 index 0000000..4eee81f Binary files /dev/null and b/Bin/PicoCalc SD/picoware/apps/games/flip_world/assets.mpy differ diff --git a/Bin/PicoCalc SD/picoware/apps/games/flip_world/general.mpy b/Bin/PicoCalc SD/picoware/apps/games/flip_world/general.mpy new file mode 100644 index 0000000..5fa3349 Binary files /dev/null and b/Bin/PicoCalc SD/picoware/apps/games/flip_world/general.mpy differ diff --git a/Bin/PicoCalc SD/picoware/apps/games/flip_world/player.mpy b/Bin/PicoCalc SD/picoware/apps/games/flip_world/player.mpy new file mode 100644 index 0000000..1c419f2 Binary files /dev/null and b/Bin/PicoCalc SD/picoware/apps/games/flip_world/player.mpy differ diff --git a/Bin/PicoCalc SD/picoware/apps/games/flip_world/run.mpy b/Bin/PicoCalc SD/picoware/apps/games/flip_world/run.mpy new file mode 100644 index 0000000..38dd28a Binary files /dev/null and b/Bin/PicoCalc SD/picoware/apps/games/flip_world/run.mpy differ diff --git a/Bin/PicoCalc SD/picoware/apps/games/flip_world/sprite.mpy b/Bin/PicoCalc SD/picoware/apps/games/flip_world/sprite.mpy new file mode 100644 index 0000000..b4f8d40 Binary files /dev/null and b/Bin/PicoCalc SD/picoware/apps/games/flip_world/sprite.mpy differ diff --git a/Bin/PicoCalc SD/picoware/apps/games/free_roam/__init__.py b/Bin/PicoCalc SD/picoware/apps/games/free_roam/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Bin/PicoCalc SD/picoware/apps/games/free_roam/dynamic_map.mpy b/Bin/PicoCalc SD/picoware/apps/games/free_roam/dynamic_map.mpy new file mode 100644 index 0000000..00a49ba Binary files /dev/null and b/Bin/PicoCalc SD/picoware/apps/games/free_roam/dynamic_map.mpy differ diff --git a/Bin/PicoCalc SD/picoware/apps/games/free_roam/game.mpy b/Bin/PicoCalc SD/picoware/apps/games/free_roam/game.mpy new file mode 100644 index 0000000..4facc5b Binary files /dev/null and b/Bin/PicoCalc SD/picoware/apps/games/free_roam/game.mpy differ diff --git a/Bin/PicoCalc SD/picoware/apps/games/free_roam/maps.mpy b/Bin/PicoCalc SD/picoware/apps/games/free_roam/maps.mpy new file mode 100644 index 0000000..eb69dfa Binary files /dev/null and b/Bin/PicoCalc SD/picoware/apps/games/free_roam/maps.mpy differ diff --git a/Bin/PicoCalc SD/picoware/apps/games/free_roam/player.mpy b/Bin/PicoCalc SD/picoware/apps/games/free_roam/player.mpy new file mode 100644 index 0000000..50cbcc0 Binary files /dev/null and b/Bin/PicoCalc SD/picoware/apps/games/free_roam/player.mpy differ diff --git a/Bin/PicoCalc SD/picoware/apps/games/free_roam/sprite.mpy b/Bin/PicoCalc SD/picoware/apps/games/free_roam/sprite.mpy new file mode 100644 index 0000000..98584f1 Binary files /dev/null and b/Bin/PicoCalc SD/picoware/apps/games/free_roam/sprite.mpy differ diff --git a/Bin/PicoCalc SD/picoware/apps/games/game_of_life.mpy b/Bin/PicoCalc SD/picoware/apps/games/game_of_life.mpy new file mode 100644 index 0000000..fb039bc Binary files /dev/null and b/Bin/PicoCalc SD/picoware/apps/games/game_of_life.mpy differ diff --git a/Bin/PicoCalc SD/picoware/apps/hello_color.mpy b/Bin/PicoCalc SD/picoware/apps/hello_color.mpy new file mode 100644 index 0000000..1da9fb4 Binary files /dev/null and b/Bin/PicoCalc SD/picoware/apps/hello_color.mpy differ diff --git a/Bin/PicoCalc SD/picoware/apps/keyboard-simple.mpy b/Bin/PicoCalc SD/picoware/apps/keyboard-simple.mpy new file mode 100644 index 0000000..54008db Binary files /dev/null and b/Bin/PicoCalc SD/picoware/apps/keyboard-simple.mpy differ diff --git a/Bin/PicoCalc SD/picoware/apps/loading-simple.mpy b/Bin/PicoCalc SD/picoware/apps/loading-simple.mpy new file mode 100644 index 0000000..51952e0 Binary files /dev/null and b/Bin/PicoCalc SD/picoware/apps/loading-simple.mpy differ diff --git a/Bin/PicoCalc SD/picoware/apps/menu-simple.mpy b/Bin/PicoCalc SD/picoware/apps/menu-simple.mpy new file mode 100644 index 0000000..66dc862 Binary files /dev/null and b/Bin/PicoCalc SD/picoware/apps/menu-simple.mpy differ diff --git a/Bin/PicoCalc SD/picoware/apps/random-object.mpy b/Bin/PicoCalc SD/picoware/apps/random-object.mpy new file mode 100644 index 0000000..d68d006 Binary files /dev/null and b/Bin/PicoCalc SD/picoware/apps/random-object.mpy differ diff --git a/Bin/PicoCalc SD/picoware/apps/screensavers/Bubble Universe.mpy b/Bin/PicoCalc SD/picoware/apps/screensavers/Bubble Universe.mpy new file mode 100644 index 0000000..3a79326 Binary files /dev/null and b/Bin/PicoCalc SD/picoware/apps/screensavers/Bubble Universe.mpy differ diff --git a/Bin/PicoCalc SD/picoware/apps/screensavers/Clock.mpy b/Bin/PicoCalc SD/picoware/apps/screensavers/Clock.mpy new file mode 100644 index 0000000..5b0903f Binary files /dev/null and b/Bin/PicoCalc SD/picoware/apps/screensavers/Clock.mpy differ diff --git a/Bin/PicoCalc SD/picoware/apps/screensavers/Cube.mpy b/Bin/PicoCalc SD/picoware/apps/screensavers/Cube.mpy new file mode 100644 index 0000000..410fb84 Binary files /dev/null and b/Bin/PicoCalc SD/picoware/apps/screensavers/Cube.mpy differ diff --git a/Bin/PicoCalc SD/picoware/apps/screensavers/DVI Bounce.mpy b/Bin/PicoCalc SD/picoware/apps/screensavers/DVI Bounce.mpy new file mode 100644 index 0000000..3c058cf Binary files /dev/null and b/Bin/PicoCalc SD/picoware/apps/screensavers/DVI Bounce.mpy differ diff --git a/Bin/PicoCalc SD/picoware/apps/screensavers/Fire Effect.mpy b/Bin/PicoCalc SD/picoware/apps/screensavers/Fire Effect.mpy new file mode 100644 index 0000000..9ab1c89 Binary files /dev/null and b/Bin/PicoCalc SD/picoware/apps/screensavers/Fire Effect.mpy differ diff --git a/Bin/PicoCalc SD/picoware/apps/screensavers/Matrix Rain.mpy b/Bin/PicoCalc SD/picoware/apps/screensavers/Matrix Rain.mpy new file mode 100644 index 0000000..3d8c32b Binary files /dev/null and b/Bin/PicoCalc SD/picoware/apps/screensavers/Matrix Rain.mpy differ diff --git a/Bin/PicoCalc SD/picoware/apps/screensavers/Patterns.mpy b/Bin/PicoCalc SD/picoware/apps/screensavers/Patterns.mpy new file mode 100644 index 0000000..fc8daff Binary files /dev/null and b/Bin/PicoCalc SD/picoware/apps/screensavers/Patterns.mpy differ diff --git a/Bin/PicoCalc SD/picoware/apps/screensavers/PicoFlower.mpy b/Bin/PicoCalc SD/picoware/apps/screensavers/PicoFlower.mpy new file mode 100644 index 0000000..7802a28 Binary files /dev/null and b/Bin/PicoCalc SD/picoware/apps/screensavers/PicoFlower.mpy differ diff --git a/Bin/PicoCalc SD/picoware/apps/screensavers/Plasma Wave.mpy b/Bin/PicoCalc SD/picoware/apps/screensavers/Plasma Wave.mpy new file mode 100644 index 0000000..617eb29 Binary files /dev/null and b/Bin/PicoCalc SD/picoware/apps/screensavers/Plasma Wave.mpy differ diff --git a/Bin/PicoCalc SD/picoware/apps/screensavers/Yin-Yang.mpy b/Bin/PicoCalc SD/picoware/apps/screensavers/Yin-Yang.mpy new file mode 100644 index 0000000..7ee6856 Binary files /dev/null and b/Bin/PicoCalc SD/picoware/apps/screensavers/Yin-Yang.mpy differ diff --git a/Bin/PicoCalc SD/picoware/apps/storage-simple.mpy b/Bin/PicoCalc SD/picoware/apps/storage-simple.mpy new file mode 100644 index 0000000..113c304 Binary files /dev/null and b/Bin/PicoCalc SD/picoware/apps/storage-simple.mpy differ diff --git a/Bin/PicoCalc SD/picoware/apps/textbox-simple.mpy b/Bin/PicoCalc SD/picoware/apps/textbox-simple.mpy new file mode 100644 index 0000000..ec64b7c Binary files /dev/null and b/Bin/PicoCalc SD/picoware/apps/textbox-simple.mpy differ