| Crates.io | shrimpl |
| lib.rs | shrimpl |
| version | 0.5.5 |
| created_at | 2025-11-17 01:02:09.775668+00 |
| updated_at | 2025-12-11 16:07:05.166716+00 |
| description | Shrimpl programming language and LSP |
| homepage | |
| repository | https://github.com/Shrimpl-Language/shrimpl-language |
| max_upload_size | |
| id | 1936091 |
| size | 738,547 |
Shrimpl is a beginner‑friendly programming language designed to bridge the gap between visual languages (like Scratch) and general‑purpose languages (like Python or JavaScript). It aims to be:
Shrimpl programs are interpreted by a Rust‑based runtime that can run on most platforms. A companion Language Server (LSP), VS Code extension, and browser‑based API Studio make it easy to experiment, debug, and explore programs interactively.
Shrimpl 0.5.x adds several important features:
openai_chat, openai_chat_json, openai_mcp_call).if / elif / else expressions, and repeat N times loops.config/config.<env>.json) for server options, JWT auth, request validation, and optional static type annotations.jwt_sub.shrimpl.lock) capturing version and hash information.model declarations into real tables in shrimpl.db, plus Shrimpl built‑ins for inserts and lookups.shrimpl-lsp in the official VS Code extension, with auto‑selected binaries per platform and a simple shrimpl.lsp.path override.The goal is to keep Shrimpl simple enough for kids and beginners, while gradually introducing real‑world concepts like APIs, authentication, validation, persistence, and types.
model blocks and let Shrimpl handle the SQLite database and migrations.Shrimpl supports:
if / elif / else, repeat)Runtime checks catch errors wherever possible and report clear messages.
Static analysis warns about:
JSON Schema validation rejects malformed requests before Shrimpl code runs.
A simple type checker can detect type mismatches between annotated functions and their bodies.
The ORM is initialized once at server startup, and failures are treated as runtime errors you can log and inspect rather than silent data corruption.
Diagnostics are visible both in the terminal, in __shrimpl/diagnostics, and inside editor integrations via the LSP.
Install Rust (if it is not already installed):
rustup.Install Shrimpl using Cargo:
cargo install shrimpl
Alternatively, build the interpreter from source in this repository:
cargo build --release
The resulting binary will be in target/release/shrimpl.
Replit deployments (optional):
replit.nix..replit to run the Shrimpl server (for example cargo run --bin shrimpl -- --file app.shr run).In the project directory, create a file named app.shr. Shrimpl scripts always use the .shr extension.
Put this minimal program in app.shr:
server 3000
endpoint GET "/": "Hello, Shrimpl!"
Run the program:
shrimpl --file app.shr run
This will:
app.shr.config/config.<env>.json (if present).model declarations.Open a browser and navigate to:
http://localhost:3000/
The response should be:
Hello, Shrimpl!
To only check syntax and diagnostics (without running the server or touching the database), use:
shrimpl --file app.shr check
To inspect all static diagnostics as JSON, use:
shrimpl --file app.shr diagnostics
This is the same data shown in the API Studio diagnostics panel.
Shrimpl programs start from a single entry file, usually app.shr.
To keep larger projects organized, Shrimpl supports a simple import mechanism:
# app.shr
server 3000
import "users.shr"
import "math_utils.shr"
endpoint GET "/": "Welcome to Shrimpl"
In users.shr:
# users.shr
func user_greeting(name):
"Hello, " + name
endpoint GET "/hello/:name":
user_greeting(name)
Notes:
import "relative/path/file.shr" inlines the contents of the other file.a.shr importing b.shr which imports a.shr) are ignored after the first visit.model declarations can live in any imported file; they are collected into a global Program.models list and fed into the ORM at server startup.config/config.<env>.json)Shrimpl loads configuration from a JSON file based on the current environment:
SHRIMPL_ENV or defaults to "dev".config/config.<env>.json (for example config/config.dev.json).A typical config file:
{
"server": {
"port": 3000,
"tls": false
},
"auth": {
"jwt_secret_env": "SHRIMPL_JWT_SECRET",
"protected_paths": ["/secure", "/admin"],
"allow_missing_on": ["/health"]
},
"validation": {
"schemas": {
"/login": {
"type": "object",
"required": ["email", "password"],
"properties": {
"email": { "type": "string", "format": "email" },
"password": { "type": "string", "minLength": 8 }
}
}
}
},
"types": {
"functions": {
"add": {
"params": ["number", "number"],
"result": "number"
}
}
},
"secrets": {
"env": {
"OPENAI": "SHRIMPL_OPENAI_API_KEY"
}
},
"values": {
"greeting": "Hello Shrimpl",
"threshold": 0.75,
"debug": true
}
}
What each section controls:
server: Port and TLS flag (can override the server declaration in Shrimpl code).auth: JWT configuration and which paths require authentication.validation: Per‑path JSON Schemas for request body validation.types: Type annotations for functions (used by the static type checker).secrets.env: Mapping from logical secret names to environment variable names.values: Arbitrary key/value pairs accessible via built‑ins (if enabled in the runtime).shrimpl.lock)When the program runs, the runtime may create a shrimpl.lock file that captures:
SHRIMPL_ENV).app.shr).This file is informational. It is safe to delete; it will be regenerated when needed.
A Shrimpl program that exposes HTTP endpoints must declare a server:
server 3000
server declaration is allowed per program.endpoint declarations.Configuration from config/config.<env>.json can override the port and TLS flag if present. Under the hood, the CLI entrypoint:
shrimpl_config::init() to load the environment‑specific JSON config.shrimpl_config::apply_server_to_program(&mut program) so config values can override the server declaration.orm::init_global_orm(&program) to initialize the SQLite‑backed ORM and run migrations for any model declarations.program.server settings.From a user’s perspective this happens automatically when you run:
shrimpl --file app.shr run
To serve HTTPS directly from Shrimpl:
server 3000 tls
In this mode the runtime expects certificate files, configured via environment variables:
SHRIMPL_TLS_CERT (defaults to cert.pem)SHRIMPL_TLS_KEY (defaults to key.pem)Both files should be PEM‑encoded. When TLS is enabled, the server binds HTTPS on 0.0.0.0:<port>.
Endpoints declare HTTP routes. An endpoint has:
GET or POST."/hello/:name".The body expression can live on the same line as the endpoint or on the following indented line.
endpoint GET "/": "Hello, Shrimpl!"
Path segments starting with : become variables in the endpoint body:
endpoint GET "/hello/:name":
"Hello " + name
GET /hello/Aisenname becomes "Aisen"."Hello Aisen".Query parameters are also exposed as variables:
endpoint GET "/greet":
"Hello " + name
/greet?name=Aisenname is set to "Aisen".If a path parameter and query parameter share a name, the path parameter wins.
body VariableFor POST endpoints, the request body is exposed as a special variable named body:
endpoint POST "/echo":
body
body.body is simply the raw text.This is especially useful together with the ORM built‑ins:
# JSON in body is inserted into the User model
endpoint POST "/orm/users": orm_insert("User", body)
# Path parameter id is passed directly as JSON scalar (e.g. "1" → 1)
endpoint GET "/orm/users/:id": orm_find_by_id("User", id)
Here the HTTP layer provides body and id as strings, and the ORM built‑ins handle JSON parsing and type conversion. Combined with JSON Schema validation, this creates a simple but realistic request → validation → persistence pipeline.
The endpoint body can return text or JSON.
# Plain text
endpoint GET "/text": "Just a string"
# Constant JSON
endpoint GET "/info":
json { "name": "Shrimpl", "version": 0.5 }
When using json { ... }, the body must be a constant JSON object; expressions are not evaluated inside the JSON literal.
When using AI helpers (openai_chat, openai_chat_json), the return value is a string. The server sends it as a text response unless it is itself JSON.
jwt_sub, jwt_scope, jwt_role)When JWT auth is enabled (see the Authentication and JWT section), Shrimpl automatically injects three variables into every request:
jwt_sub: subject / user id (string)jwt_scope: optional scope stringjwt_role: optional role stringThese variables always exist and default to empty strings if no token is present or if the path is not protected.
Example:
endpoint GET "/secure/profile":
if jwt_sub == "":
"No user bound to this token"
else:
"Hello user " + jwt_sub
This allows endpoints to read identity information without worrying about missing variables.
Shrimpl expressions are intentionally small and consistent. This version introduces booleans, comparisons, logical operators, and expression‑level control flow.
Supported literals:
42, 3.14, -10"Hello", "abc123"true, falsejson { "key": 123 }Booleans are first‑class values in Shrimpl:
endpoint GET "/bools":
if true:
"This is always returned"
else:
"Never reached"
Truthiness rules used in if, and, or, and repeat:
Bool: true and false behave as expected.Number: 0.0 is false; any other number is true.String: "" is false; any other string is true.Names start with a letter or underscore and may contain letters, digits, or underscores.
Common sources:
:id → id)?foo=bar → foo)body, jwt_sub, jwt_scope, jwt_roleShrimpl supports arithmetic, comparison, and logical operators.
Arithmetic:
+, -, *, /+ is a string, Shrimpl performs string concatenation instead of numeric addition.Comparison operators:
==, !=, <, <=, >, >=Logical operators:
and, orOperator precedence (from tightest to loosest):
*, /+, -==, !=, <, <=, >, >=andorExample:
endpoint GET "/logic":
if 2 * 3 + 1 == 7 and true:
"Math works"
else:
"Something is off"
if / elif / else as an ExpressionShrimpl uses an expression‑oriented if:
if condition1: expr1
elif condition2: expr2
else: expr3
This form can appear anywhere an expression is allowed:
endpoint GET "/age":
if number(age) < 13:
"child"
elif number(age) < 18:
"teen"
else:
"adult"
Rules:
else, the result is an empty string ("").repeat N times: exprA bounded loop expression:
repeat N times: body_expr
Example:
func repeat_greet(name, n):
repeat number(n) times:
"Hello " + name # result of last iteration is returned
endpoint GET "/repeat-greet":
repeat_greet(name, n)
Behavior:
N is evaluated once and converted to a number."" if N == 0.Define reusable computations with the func keyword:
func greet(name):
"Hello " + name
Rules:
func name(param1, param2, ...): expressionExample usage:
endpoint GET "/welcome/:name":
greet(name) + "!"
AI‑friendly wrapper:
func tutor(topic):
openai_chat("Explain this topic for a beginner: " + topic)
endpoint GET "/tutor/:topic":
tutor(topic)
Shrimpl supports classes with static methods for grouping related functions:
class Math:
double(x): x * 2
square(x): x * x
class Name: followed by indented method definitions.methodName(params): expression.self and act like static helpers.Usage:
endpoint GET "/double/:n":
Math.double(number(n))
Classes can also be used to group domain‑specific helpers, such as formatting routines or domain logic.
These helpers operate on basic values:
| Built‑in | Description |
|---|---|
number(x) |
Convert string or number x to a floating‑point number. |
string(x) |
Convert any value to a string. |
len(x) |
Length of a string. |
upper(x) |
String to uppercase. |
lower(x) |
String to lowercase. |
sum(a,b,...) |
Sum of numbers. |
avg(a,b,...) |
Average of numbers. |
min(a,b,...) |
Minimum of numbers. |
max(a,b,...) |
Maximum of numbers. |
openai_set_api_key(k) |
Set/override the OpenAI API key used by AI helpers. |
openai_set_system_prompt(p) |
Set a global system prompt (role) for AI helpers. |
openai_chat(msg) |
Call an OpenAI chat model; return reply text. |
openai_chat_json(msg) |
Call an OpenAI chat model; return full JSON as text. |
openai_mcp_call(server, tool, args) |
Experimental helper for MCP/tool‑calling style workflows. |
Helpers for calling external APIs:
| Function | Description |
|---|---|
http_get(url) |
Send HTTP GET to url, return raw body as a string. |
http_get_json(url) |
GET url, parse response as JSON, and return pretty‑printed JSON string. |
Example:
endpoint GET "/pokemon/:id":
http_get_json("https://pokeapi.co/api/v2/pokemon/" + id)
Helpers for numeric arrays:
| Function | Description |
|---|---|
vec(a, b, ...) |
Create a JSON array [a, b, ...]. Numeric strings are converted to numbers. |
tensor_add(a, b) |
Element‑wise add two JSON arrays of equal length; returns a JSON array. |
tensor_dot(a, b) |
Dot product of two JSON arrays of equal length; returns a number. |
Example:
endpoint GET "/dot":
tensor_dot(
vec(number(ax), number(ay)),
vec(number(bx), number(by))
)
Dataframes are represented as JSON objects:
{
"columns": ["name", "age"],
"rows": [["Alice", 30], ["Bob", 25]]
}
| Function | Description |
|---|---|
df_from_csv(url) |
Download CSV from url and return dataframe JSON. Numbers become floats. |
df_head(df_json, n) |
Return first n rows of the dataframe. |
df_select(df_json, cols) |
Return new dataframe with only specified columns (comma‑separated string). |
Examples:
endpoint GET "/load":
df_from_csv("https://people.sc.fsu.edu/~jburkardt/data/csv/hw_200.csv")
endpoint GET "/head":
df_head(df, 5)
Simple linear regression is supported.
Model JSON shape:
{ "kind": "linreg", "a": 2.0, "b": 0.0 }
| Function | Description |
|---|---|
linreg_fit(xs_json, ys_json) |
Train regression model from arrays xs and ys (JSON arrays). |
linreg_predict(model_json, x) |
Predict y from a model and input x; returns a number. |
Examples:
endpoint GET "/train":
linreg_fit(xs, ys)
endpoint GET "/predict":
linreg_predict(model, number(x))
Shrimpl 0.5.5 introduces a minimal ORM layer backed by SQLite. It turns model declarations in Shrimpl code into real tables in a shrimpl.db file and exposes simple built‑ins for inserts and lookups.
At server startup (shrimpl --file app.shr run):
model declarations into Program.models.orm::init_global_orm(&program) opens shrimpl.db (in the current working directory) or creates it if it does not exist.program.models into its own internal map.CREATE TABLE IF NOT EXISTS statement with the proper columns, primary key, and nullability.From Shrimpl code you do not call init_global_orm directly; instead you use the following built‑ins:
| Function | Description |
|---|---|
orm_insert(model_name, record_json_string) |
Insert a record into the table backing the given model. Returns the SQLite rowid as a string. |
orm_find_by_id(model_name, id_json_string) |
Look up a row by primary key. Returns a JSON string of the record, or null when no row is found. |
Both functions expect JSON strings, not structured objects, because Shrimpl expressions are string‑based. Typical usage combines them with HTTP request variables.
Models are declared in Shrimpl using the model keyword:
model User:
id: int pk
email: string
name: string
age?: int
model Task:
id: int pk
title: string
status: string
payload?: string
Key points:
Each model has a name (User, Task) and a list of fields.
A field has:
id, email, age),int, string, bool, number, text, etc.),pk (primary key) and ? (optional).During ORM setup, each field becomes a SQLite column.
The type mapping from Shrimpl to SQLite is:
int, integer → INTEGERnumber, float, double, real → REALbool, boolean → INTEGER (0/1)string, text → TEXTTEXT with a warningFields marked as pk become PRIMARY KEY. Fields that are not optional (?) become NOT NULL.
orm_insert(model_name, record_json)orm_insert inserts a new row into the table backing the given model.
Signature:
orm_insert(model_name, record_json)
model_name: the Shrimpl model name as a string, for example "User".record_json: a JSON string representing a flat object whose keys are field names.Internally, the ORM:
ModelDef for model_name.record_json into a JSON object.INSERT INTO table (cols...) VALUES (?, ?, ...) statement.rowid as a string.Example endpoint that creates users from a JSON body:
model User:
id: int pk
email: string
name: string
age?: int
server 3000
endpoint POST "/users":
# Expecting a JSON body like {"id": 1, "email": "...", "name": "..."}
orm_insert("User", body)
Example request:
POST /users
Content-Type: application/json
{"id": 1, "email": "alice@example.com", "name": "Alice"}
Response body (string):
1
This is the SQLite rowid. You can return it directly or wrap it in your own JSON:
endpoint POST "/users":
"{\"inserted_id\": " + orm_insert("User", body) + "}"
If the JSON does not contain any fields that match the model’s fields, or if the SQL insert fails, orm_insert returns an error string.
orm_find_by_id(model_name, id_json)orm_find_by_id retrieves a single row by primary key and returns it as a JSON string.
Signature:
orm_find_by_id(model_name, id_json)
model_name: the Shrimpl model name as a string.id_json: a JSON string representing the primary key value. For simple integer keys, this can be as simple as "1".Internally, the ORM:
ModelDef for model_name.pk.id_json to a JSON value and then a SQLite value.SELECT * FROM table WHERE pk = ? LIMIT 1.If no row is found, orm_find_by_id returns null (the Shrimpl null value).
Example endpoints:
# Returns a JSON string representing the user, or null if not found
endpoint GET "/users/:id":
orm_find_by_id("User", id)
# Returns a more decorated message
endpoint GET "/users/:id/info":
user_json = orm_find_by_id("User", id)
if user_json == null:
"User not found"
else:
"Found user: " + user_json
This pattern pairs well with validation on write (using JSON Schema) and lightweight checks on read (for example verifying the string is not null).
AI helpers are optional built‑ins that talk to OpenAI models.
If no API key is configured and a program calls these helpers, they return an error string instead of crashing. This keeps the language safe for classrooms.
The helpers look for an API key in this order:
SHRIMPL_OPENAI_API_KEYOPENAI_API_KEYopenai_set_api_key("...")The simplest setup:
export SHRIMPL_OPENAI_API_KEY="sk-example..."
shrimpl --file app.shr run
Alternatively, set the key from Shrimpl code (for example in a teacher‑only endpoint):
endpoint POST "/setup":
openai_set_api_key(secret)
Models behave differently depending on their system prompt. Shrimpl exposes this via:
openai_set_system_prompt("You are a friendly Shrimpl tutor for kids.")
Once set, this prompt is included in all subsequent AI calls.
openai_chat(message)openai_chat is the simplest helper:
gpt-4.1-mini).Example endpoint:
server 3000
endpoint GET "/chat/:msg":
openai_chat("Student says: " + msg)
Calling:
GET /chat/What%20is%20a%20variable%3F
returns an explanation generated by the model.
openai_chat_json(message)openai_chat_json is similar to openai_chat, but returns the full JSON response as a pretty‑printed string. This is useful for debugging or advanced teaching.
Example:
endpoint GET "/raw_chat/:msg":
openai_chat_json(msg)
openai_mcp_call(server_id, tool_name, args) (Experimental)openai_mcp_call is designed for advanced tool‑calling/MCP workflows.
Signature:
openai_mcp_call(server, tool, args)
server: which tool server/config to talk to.tool: tool name.args: arguments as a JSON string.Example (simplified):
endpoint POST "/tools/query":
openai_mcp_call("math-server", "solve_equation", args)
For most beginner use cases, focusing on openai_chat and openai_chat_json is enough.
If an AI call fails (missing key, network issues, etc.), the helpers return an error message string. It can be shown directly or wrapped:
endpoint GET "/safe_chat/:msg":
"AI says: " + openai_chat(msg)
Shrimpl supports optional JWT‑based authentication configured via config/config.<env>.json.
In the config file:
"auth": {
"jwt_secret_env": "SHRIMPL_JWT_SECRET",
"protected_paths": ["/secure", "/admin"],
"allow_missing_on": ["/health"]
}
Fields:
jwt_secret_env: name of the environment variable that holds the HMAC secret for verifying JWTs.protected_paths: list of path prefixes that require a valid JWT (for example "/secure").allow_missing_on: list of path prefixes that are always accessible even if they overlap with protected paths (for example "/health").At runtime:
Protected paths expect an Authorization: Bearer <token> header.
Tokens are validated using the configured secret.
On failure, the server returns 401 JSON errors:
{"error":"missing bearer token"}{"error":"unauthorized","detail":"..."}Valid tokens are decoded into a claim set that includes:
sub → exposed as jwt_subscope → exposed as jwt_scoperole → exposed as jwt_roleThese variables are always defined and default to "" when no token is present or when auth is not required.
Example:
endpoint GET "/secure/hello":
if jwt_sub == "":
"You are not authenticated"
else:
"Hello, user " + jwt_sub
This allows beginners to work with authentication concepts without needing to parse headers manually.
Shrimpl can validate JSON request bodies using JSON Schema defined in the config file.
In config/config.<env>.json:
"validation": {
"schemas": {
"/login": {
"type": "object",
"required": ["email", "password"],
"properties": {
"email": { "type": "string", "format": "email" },
"password": { "type": "string", "minLength": 8 }
}
}
}
}
schemas are Shrimpl endpoint paths (for example "/login").For a POST to a path with a schema:
400 {"error":"invalid_json","detail":"..."}.400 with {"error":"validation_failed","detail":"..."}.body variable and passed to Shrimpl code.Sanitization currently:
Example endpoint that assumes valid JSON:
endpoint POST "/login":
"Sanitized login payload: " + body
If a schema is misconfigured, the server responds with 500 and {"error":"schema_compile_error","detail":"..."} to avoid blaming the user.
When combining validation and the ORM, a common pattern is:
# config/config.dev.json
"validation": {
"schemas": {
"/orm/users": {
"type": "object",
"required": ["id", "email", "name"],
"properties": {
"id": {"type": "integer"},
"email": {"type": "string", "format": "email"},
"name": {"type": "string"},
"age": {"type": "integer"}
}
}
}
}
# app.shr
model User:
id: int pk
email: string
name: string
age?: int
endpoint POST "/orm/users":
# body is now validated and sanitized JSON
orm_insert("User", body)
This keeps database writes safe and predictable without adding complexity to Shrimpl code.
This section summarizes how the ORM, HTTP server, and Shrimpl code work together in a typical application.
When you run:
shrimpl --file app.shr run
The runtime:
Parses app.shr (and any imported .shr files) into an internal Program.
Reads SHRIMPL_ENV and loads config/config.<env>.json if present.
Applies config overrides to program.server (port, TLS).
Initializes the global ORM with program.models:
shrimpl.db.CREATE TABLE IF NOT EXISTS ... for each model.Starts the HTTP server (actix-web).
For each incoming request, wires request data into variables (body, jwt_*, path and query parameters).
Evaluates the endpoint body as a Shrimpl expression.
If ORM initialization fails (for example invalid database path), Shrimpl logs a message like:
[shrimpl-orm] failed to initialize ORM: <details>
but the server still starts so you can debug the issue from Shrimpl code or logs.
app.shr Demo with ORM, Auth, Validation, and Rate LimitingA more complete app.shr might look like this (simplified):
server 3000
model User:
id: int pk
email: string
name: string
age?: int
model Task:
id: int pk
title: string
status: string
payload?: string
endpoint POST "/orm/users": orm_insert("User", body)
endpoint GET "/orm/users/:id": orm_find_by_id("User", id)
endpoint POST "/orm/tasks": orm_insert("Task", body)
endpoint GET "/orm/tasks/:id": orm_find_by_id("Task", id)
@rate_limit(5, 60)
endpoint GET "/limited/ping": "pong"
@rate_limit 3 10
endpoint GET "/limited/stats": "ok"
Configured with validation and auth in config/config.dev.json, this single program:
All of this is driven by Shrimpl code and JSON configuration, without writing any SQL or HTTP boilerplate.
Shrimpl includes an optional type checker that uses annotations defined in config/config.<env>.json.
Under "types" in config:
"types": {
"functions": {
"add": {
"params": ["number", "number"],
"result": "number"
},
"greet": {
"params": ["string"],
"result": "string"
}
}
}
Supported type names:
number, float, int, integer → numericstring, str → stringbool, boolean → booleananyFor each annotated function:
Checks that the number of parameters in Shrimpl matches params length.
Builds a parameter type environment from params.
Infers a simple type for the function body using:
Number, String, Bool).any).number, comparisons yield bool, and/or yield bool).if expressions (join of branch types; if mixed, treated as any).repeat expressions (type of the body or any).If the inferred body type is not assignable to the declared result, an error is produced.
Types are assignable if:
any, oractual == expected, oractual is any.Diagnostics are reported as JSON objects with fields like:
{
"kind": "error",
"scope": "function",
"name": "add",
"message": "Return type mismatch: expected number, got string"
}
Type diagnostics are included in:
shrimpl --file app.shr diagnosticsGET /__shrimpl/diagnostics (JSON)Because Shrimpl is still dynamically typed at runtime, type annotations are always optional. They provide extra feedback, not hard compilation barriers.
To return constant JSON, use the json prefix:
endpoint GET "/info":
json { "name": "Shrimpl", "version": 0.5 }
Notes:
json { ... }.For AI‑driven endpoints, typical patterns are:
# Plain text from AI
endpoint GET "/chat/:msg":
openai_chat(msg)
# Pretty‑printed JSON from AI
endpoint GET "/chat_json/:msg":
openai_chat_json(msg)
Shrimpl’s static analyzer helps keep programs clean.
Current checks include:
endpoint GET "/:id" where id is not used.The analyzer understands all expression variants, including:
Expr::Bool (boolean literals)Expr::If (if / elif / else expressions)Expr::Repeat (repeat loops)Diagnostics appear in:
shrimpl --file app.shr checkshrimpl --file app.shr diagnosticsGET /__shrimpl/diagnosticsWhen a Shrimpl server is running, open:
http://localhost:<port>/__shrimpl/ui
API Studio includes:
Endpoint Explorer
Request Panel
Response Panel
Source Panel
app.shr with syntax highlighting.Diagnostics Panel
Additional internal endpoints:
GET /__shrimpl/schema → machine‑readable schema for endpoints.GET /__shrimpl/diagnostics → diagnostics as JSON.GET /__shrimpl/source → raw app.shr contents.GET /health → simple health check returning JSON.API Studio is ideal for:
model declarations, ORM calls, and HTTP APIs interact in a single console.Shrimpl ships with a Language Server, shrimpl-lsp, that provides editor features.
server, endpoint, functions, classes, models).server, endpoint, func, class, GET, POST, model).You can still build and run the language server directly:
cargo build --bin shrimpl-lsp
cargo run --bin shrimpl-lsp
The server speaks JSON‑RPC 2.0 over stdio, which most editors can connect to.
For most users, the simplest way to use the Shrimpl language server is via the official VS Code extension.
The extension:
Registers .shr files with the shrimpl language.
Starts shrimpl-lsp automatically when you open a Shrimpl file.
Ships prebuilt shrimpl-lsp binaries under server/:
server/shrimpl-lsp-darwin-arm64server/shrimpl-lsp-linux-x64server/shrimpl-lsp-win32-x64.exeOn activation, the extension:
context.asAbsolutePath("server/<binary>")).This means:
shrimpl-lsp yourself.PATH.Advanced users can override the bundled binary using the shrimpl.lsp.path setting.
Example VS Code settings:
{
"shrimpl.lsp.path": "${workspaceFolder}/target/debug/shrimpl-lsp"
}
The extension:
${workspaceFolder} and ${workspaceFolderBasename} variables.PATH (for example "shrimpl-lsp").Diagnostic messages are written to the Shrimpl output channel, so you can see exactly which command was used and what error occurred if startup fails.
The extension exposes a configuration schema:
"configuration": {
"type": "object",
"title": "Shrimpl",
"properties": {
"shrimpl.lsp.path": {
"type": "string",
"default": "shrimpl-lsp",
"description": "Command used to start the Shrimpl language server (absolute path, workspace-relative path, or executable name on PATH)."
},
"shrimpl.lsp.debugArgs": {
"type": "array",
"items": {
"type": "string"
},
"default": [
"--log-level",
"debug"
],
"description": "Additional arguments passed to the Shrimpl language server when running in debug mode."
},
"shrimpl.trace.server": {
"type": "string",
"enum": [
"off",
"messages",
"verbose"
],
"default": "off",
"description": "Trace level for the Shrimpl language server."
}
}
}
Highlights:
shrimpl.lsp.path
shrimpl.lsp.debugArgs
--log-level debug) when VS Code is in debug mode.shrimpl.trace.server
The extension’s activate function:
On deactivation it stops the client and ensures the LSP process exits cleanly.
Sample configurations live under editors/:
editors/nvim-shrimpl/)editors/sublime/)editors/jetbrains/)Each setup wires .shr files to shrimpl-lsp and provides syntax highlighting plus diagnostics. These continue to work exactly as before; the VS Code bundling is an additional, more convenient option.
The VS Code extension can contribute a minimal icon theme:
"iconThemes": [
{
"id": "shrimpl-icons",
"label": "Shrimpl Icons",
"path": "./icon-theme.json"
}
]
icon-theme.json defines a Shrimpl icon (for example for .shr files).This means:
Shrimpl logs each HTTP request as a JSON line on stdout, including:
"info")"http-request")auth_ok flag indicating whether the request had valid auth claimsThis format makes it easy to feed logs into other tools or to demonstrate structured logging in teaching environments.
#) to explain intention, especially in tutorial code.model + ORM for persistence once students are ready to see stateful APIs, instead of introducing SQL directly.The repository is organized into clear layers:
Parser (src/parser/)
.shr source into an abstract syntax tree (AST).if / elif / else, and repeat expressions.model declarations into ModelDef structures used by the ORM.AST / Core Model (src/parser/ast.rs)
Program, EndpointDecl, FunctionDef, ClassDef, ModelDef, Expr, and more.Program.models holds all models declared in Shrimpl code.Interpreter (src/interpreter/)
orm_insert, orm_find_by_id) and wires request variables into endpoint bodies.ORM (src/orm.rs)
shrimpl.db).CREATE TABLE IF NOT EXISTS for each ModelDef on startup.Config (src/config.rs)
config/config.<env>.json.Program.server before HTTP startup.Lockfile (src/lockfile.rs)
shrimpl.lock with version, environment, entry path, and hash.Docs and Diagnostics (src/docs.rs)
/__shrimpl/schema./__shrimpl/ui.CLI (src/main.rs)
run, check, and diagnostics modes.init_global_orm before starting the HTTP server.Language Server (src/bin/shrimpl_lsp.rs)
This separation keeps language design and teaching concerns clear while allowing the runtime to grow with features like JWT auth, validation, types, AI integration, and now a minimal but useful persistence layer.
Shrimpl 0.5.x combines the simplicity of a teaching language with practical features drawn from real‑world API development:
if, elif, else, repeat)model declarations and simple built‑ins (orm_insert, orm_find_by_id)Learners can start with a few lines of code, see immediate results in the browser, and progressively discover more advanced ideas like authentication, validation, persistence, and editor tooling without leaving the language.