== Getting Started

This document predates the `pig` command line tool. Instead read `quickstart.md`
first.

All of this is subject to change, but if you want to try to run the
project right now, then this should help you get going.

=== Running inside nodejs

Requirements: Nodejs >=17

Some appetisers

[source,shell]
----
npm install -g piglet-lang@latest

# Drop into a REPL
piglet

# Run expression and exit
piglet -e '(println (time (reduce + (range 10000))))'
piglet -e "(run! println (sort (keys (find-module 'piglet:lang))))"

# Dev server for frontend dev
piglet -i piglet:node/dev-server

# Piglet Dev Protocol client for browser integration
# (Requires a running server, e.g. inside emacs)
piglet -i piglet:pdp-client

# Hot reload Piglet's own code (JS code, not Piglet code), and allow
# connecting from Chromium devtools (look for the green button, top left)
piglet --devtools

# See emitted Javascript
piglet -v
----

As shown here you can get access to the `piglet` script by installing the
`piglet-lang` package with npm/pnpm. You can also run it through npx (`npx
piglet-lang@latest`), but that adds a significant overhead to startup.

You can also just clone the repo and symlink `bin/piglet` to somewhere on your
`PATH`.

Without any arguments the `piglet` command drops you into a REPL, or can you can
specify expressions (-e), packages (-p), or modules (-i) to load and run.

Under the hood this just invokes node like this, which is itself a fairly basic
Node.js sript to load the compiler and interpret command line flags.

[source,shell]
----
node lib/piglet/node/main.mjs
----

The `piglet` wrapper script does do a couple of extra things for convenience.

* Resolves the path to `node/main.mjs` based on the location of the script, so
  you can call it from anywhere
* Calls piglet with `rlwrap` (if it's available), so you get REPL history and
  line editing
* Adds a `--devtools` flag (see below)

Command line flags:

* `-p`: register a package given a path of a directory containing a
  `package.pig` file. Does not load any actual code, but allows importing
  modules from the package with `-i` or from code. Alternatively use `:pkg:deps`
  in `package.pig`
* `-i`: import a module, using `package:module`, e.g. `-i piglet:pdp-client` or
  `-i https://example.com/my-pkg:my-mod`
* `-v`: inrease verbosity, when using the REPL this will print out intermediate
  steps in the evaluation, like the form, generated JS code, or AST

You can also pass positional arguments, which should be paths to `.pig`
script files. These will be evaluated in order.

==== Connecting from Chrome devtools

While working on Piglet itself `piglet --devtools` is very handy. Note
that `--devtools` has to be the first argument to `piglet`, since it’s
processed by the wrapper script, not by piglet itself. So if you are
passing additional arguments to Piglet, then add those at the end.

This will invoke node with the `--inspect` flag, which allows you to
connect from your Chrome devtools, and will run with
https://www.npmjs.com/package/nodemon[nodemon] which provides hot reload
(of Piglet’s own code, we don’t yet have hot reload for your Piglet
code).

==== Connecting to a PDP server

PDP stands for Piglet Dev Protocol, and is what is used for
communication between your editor and Piglet when doing interactive
programming. You may be used to languages where your editor connects to
a REPL. With PDP these roles are reversed, your editor provides the
server, and Piglet connects to it.

Currently there is only a PDP implementation for Emacs, found in
https://github.com/piglet-lang/piglet-emacs[piglet-emacs] :
https://github.com/piglet-lang/piglet-emacs/blob/main/pdp.el[pdp.el].
See below for setting up Emacs.

=== Running in the browser

Piglet itself is written to run in the browser from source without any
additional tooling or compilation, you just need something to serve up the files
over HTTP. 

==== With `piglet:node/dev-server` 

In principle you don't need any tooling to run Piglet, but when doing in-browser
dev there's a significant amount of wiring, especially when depending on other
piglet packages, or on npm packages (or piglet packages which depend on nodejs
packages). That's why we created the dev-server, which will generate an
`index.html`, rewrite your `package.pig` files so the browser knows where to
load things from, and generate an `importmap` so npm packages can be imported
with plain specifiers.

To use this, create a `package.pig` with a `:pkg:main` entry

[source,piglet]
----
;; package.pig
{:pkg:paths ["src"]
 :pkg:main webproj}
----

And create a main module:

[source,piglet]
----
;; src/webpro.pig
(module webproj)

(js:alert "it works!")
----

Now run the dev-server, and open your browser at localhost:1234.

[source,shell]
----
piglet -i piglet:node/dev-server
----

You can put static assets under `public/`, they will be served at the root.

Currently the dev-server is not really configurable yet, so there are a bunch of
behaviors that you may or may not want. Currently it sets verbosity to 0, loads
a pdp-client, and includes source-maps. Once we add a `-m`/`--main` flag to
`piglet` that will become the way to invoke the dev-server, and will allow you
to pass in command line flags.

==== Without the dev-server

What follows is how it used to work, and it's still interesting documentation to
see how to set things up yourself. The easiest way to get started without the
dev-server is by using the jsDeliver CDN, which will pull files from the npm
release:

[source,html]
----
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <script type="importmap">
      {"imports": {"astring": "https://cdn.jsdelivr.net/npm/astring@latest/dist/astring.mjs"}}
    </script>
    <script type="module" src="https://cdn.jsdelivr.net/npm/piglet-lang@0.1.27/lib/piglet/browser/main.mjs"></script>
    <script type="piglet">
      (js:console.log "Hello from Piglet 🐷!")
    </script>
  </head>
  <body>
    <div id="app"></div>
  </body>
</html>
----

The `piglet/browser/main.mjs` entry point will run any `script` tags it finds
with type="piglet". They can be inline scripts like the one above, or reference
a file to load with `src="..."`.

Notice the `importmap`, this is standard browser feature, not anything Piglet
related. Piglet requires `astring` to convert JS AST (known as ESTree), to JS.
When we `import "astring"` the browser needs to know where `astring` is supposed
to come from. In Node.js there's automatic resolution based on `node_modules`,
in the browser you use `importmap` to specify a URL.

You can use `importmap` to make other JS libraries available as well, which you
can then import with a simple short name.

[source,html]
----
<script type="importmap">
  {"imports":
   {"astring": "https://cdn.jsdelivr.net/npm/astring@latest/dist/astring.mjs",
    "solid-js": "https://cdn.jsdelivr.net/npm/solid-js@latest/dist/solid.js"}}
</script>
<script type="module" src="https://cdn.jsdelivr.net/npm/piglet-lang@latest/lib/piglet/browser/main.mjs"></script>
<script type="piglet">
  (module main
   (:import [solid :from "solid-js"]))

  (js:console.log (js:Object.keys (.-vars (find-module 'solid))))
</script>
----

To load JS files that are included in your own package, use relative paths:
`(:import [util :from "util.js"])`. Note that these have to be valid ES6
modules.

Using `<script type="piglet">` will evaluate the given code (inline or from a
file) as scripts, simply evaluating forms from top to bottom. If you want to
bundle a bunch of modules in a package, and/or load modules from other packages,
then point piglet first at the package location, which contains its
`package.pig`

Say you have the following `package.pig` at `http://example.com/my/pkg/package.pig`:

[source,piglet]
----
{:pkg:name https://packages.example.com/my-pkg
 :pkg:paths ["src"]}
----

And a module at `http://example.com/my/pkg/src/main.pig`.

Then you could load it with:

[source,html]
----
<script type="piglet">
  ;; Load //example.com/my/pkg/package.pig
  (load-package "//example.com/my/pkg")
  ;; Load the module
  (require 'https://packages.example.com/my-pkg:main)
</script>
----

Your package can in turn depend on other packages, which will get loaded and
resolved too.

A piglet web project could look like this:

[source]
----

.
├── index.html
├── package.pig
└── src
    └── main.pig
----

Where from `index.html` you do `(load-package js:window.location)`, and then
continue to require your main module.

The `http-server` package can be useful here. Note that you'll have to run a
separate http-sever in each piglet package you're depending on. Make sure to
enable `--cors` so requests between them don't get blocked.

[source,shell]
----
pnpm i http-server -g
http-server --port 8000 --cors
----

==== Source Maps

The BrowserCompiler is able to generate source maps, which are inlined into the evaluated code. To enable this, make sure the `source-map` library is loaded.

[source,html]
----
<script type="application/javascript" src="https://unpkg.com/source-map@0.7.3/dist/source-map.js"></script>
----

==== Using the bookmarklet

We have a bookmarklet (a little bit of JavaScript that you can add to your
browser as a bookmark) which can inject Piglet and a PDP client into any page,
so that you can then interact with it (e.g. inspect the DOM) from your
interactive editor. Open this page, it contains the bookmarklet link which you
can drag to your browser bookmarks toolbar.

link:https://unpkg.com/piglet-lang@latest/doc/bookmarklet.html[Bookmarklet]

Then make sure your PDP server is running in your editor (see below), and then
click on the bookmarklet to inject Piglet into the page, and connect to your
editor. Tip: the `piglet:dom` module has some handy helpers for doing DOM
querying and manipulation.

Note that this won't work on all pages, depending on their CORS and CSP
policies. If you want to use it to extract data out of HTML, and the site won't
let you do it, then try saving the html as a file, and opening that file in your
browser.

(We might turn this into a browser extension later on, which should allow us to
bypass some of these limitations.)

=== Setting up Emacs

Requirements: Emacs 29 compiled with tree-sitter.

Piglet has a https://tree-sitter.github.io/tree-sitter/[tree-sitter]
grammar, which any editor integration SHOULD use. This ensures that all
editors have a consistent way of parsing and handling piglet code.
Tree-sitters grammars are easy to define, and the generated parsers are
very fast and can be used in many contexts, either by compiling to
C/Rust or to WASM.

Most editors have tree-sitter integration, and defining e.g. indentation
or syntax highlighting based on a tree-sitter grammar is usually much
easier than doing it with an editor’s native language handling support.

Emacs’s has tree-sitter support since Emacs 29, which is not (at time of
writing) officially out yet, so you need to build it yourself, and you
need to make sure that it gets compiled with tree-sitter enabled.

For Debian/Ubuntu based distros, install `libtree-sitter0` (and possibly
also `libtree-sitter-dev`).

[source,shell]
----
git clone https://github.com/emacs-mirror/emacs
cd emacs
git co emacs-29
./autogen.sh
./configure --with-tree-sitter
# (you can add more flags here, like --with-native-compilation)
make -j4
# replace 4 with the number of cores you have
sudo make install
----

Now you need to set up piglet-emacs, which contains the `piglet-mode`
major mode, and a PDP (piglet dev protocol) server implementation.

If you are using the
https://github.com/radian-software/straight.el[Straight] package manager
for Emacs then you can get `piglet-emacs` from Corgi’s package
repository (you don’t need to use any other part of Corgi). For
instance:

[source,emacs-lisp]
----
;; ... bootstrap straight ...

(straight-use-package 'use-package)

(setq straight-use-package-by-default t)

(use-package corgi-packages
  :straight (corgi-packages
             :type git
             :host github
             :repo "corgi-emacs/corgi-packages"))
             
(use-package piglet-emacs)
----

Open a `.pig` file and you should see syntax highlighting and have
indentation support. Congrats!

==== Emacs PDP server

For interactive programming you start a PDP server inside Emacs (this is
a http server listening for websocket connections), then connect to it
from Piglet.

....
M-x pdp-start-server!
....

Now start piglet with `piglet -i piglet:pdp-client`, and you should see
a (noisy) message in your minibuffer. Now you can use

- `pdp-eval-last-sexp`
- `pdp-eval-outer-sexp`
- `pdp-eval-buffer`
- `pdp-eval-region`
- `pdp-jump-to-definition`

Note that jump-to-definition is a standalone command, not backed by Xref, but we
do push a marker onto xref's marker stack before jumping, so you can jump back
as you are used to. When using piglet on a web project you can enable the
`url-handler-mode` globalized minor mode, to make Emacs load files over the HTTP
when jumping to definition. (In such a scenario the `:location` metadata on the
var is a URL, rather than a filename.)

Note that you can open a file and do a `pdp-eval-buffer` to have all vars
in that module reflect the location as reported by Emacs.

PDP messages carry the current package, module, buffer-file-name, source
location, etc. This ensures that when you eval a form in a module's buffer, it's
evaluated in that module, not whatever module is currently active or most
recently loaded. We don't auto-eval module declarations though, so if you have
any imports you will have to eval the module form before those are available.

Reporting the filename and source location ensures that var metadata is correct,
which in turn ensures that jump-to-definition can do its job.

=== Creating your first project

[source,shell]
----
mkdir my-proj
cd my-proj
cat <<EOF > package.pig
{;; :pkg:name https://example.com/my-pkg  ; optional
 :pkg:paths ["src"]
 :pkg:deps {}}
EOF

mkdir src
echo '(module hello) (println "Working!")' > src/hello.pig
----

[source,shell]
----
piglet -i hello
# or
piglet -i https://example.com/my-pkg:hello
----

=== Declaring dependencies

Currently we can only handle dependencies that exist as packages on the
local filesystem. For this the syntax in `package.pig` is

[source,piglet]
----
{:pkg:deps {some-alias {:pkg:location "../other-package-dir"}}}
----

This assumes that there’s a `package.pig` in `../other-package-dir`.
With this in place you can start importing modules from this other
package in your modules, using the `some-alias` alias.

[source,piglet]
----
(module hello
  (:import
    [m :as some-alias:some-module]))
----

You can also ignore the alias, and use a full identifier. So say
`other-package-dir/package.pig` contains
`{:pkg:name https://example.com/other-package}`:

[source,piglet]
----
(module hello
  (:import
    [m :as https://example.com/other-package:some-module]))
----
