Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

luiggi

Package Overview
Dependencies
Maintainers
1
Versions
5
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

luiggi

Educational toy programming language implemented in JS

  • 0.9.12
  • unpublished
  • latest
  • Source
  • npm
  • Socket score

Version published
Maintainers
1
Created
Source

Table of contents

Introduction

Luiggi is an educational toy programming language implemented in JS, with four main goals:

  • Show a simple and relatively efficient implementation of a programming language:
    • Simple lexer (tokenizer) implemented directly in JS.
    • Simple parser and bytecode compiler (without any intermediate AST).
    • Simple virtual machine, reusing JS data structures.
  • Use a simple syntax reminescent of Python and BASIC, with minimal overhead.
  • Provide a ready-to-use standard library with everything needed to implement simple 2D games (based on Raylib).
  • Easy to use compilation to desktop (Windows, Linux, macOS) and web-ready WASM binaries, by packing the bytecode interpreter and user code.

The language uses dynamic typing for implementation simplicity.

Syntax

Comments

Comments start with # and end at the end of the line:

# This is a comment
x = 1 # This is also a comment

Multi-line comments are explicitly not supported, in order to support context-independent line-by-line parsing.

Identifiers and keywords

Identifiers start with a letter or an underscore, and may contain letters, digits and underscores. Identifiers starting with a double underscore __ are not allowed. Case is sensitive.

Reserved keywords cannot be used as variable or function identifiers. The following list shows all reserved keywords used in Luiggi:

func if then else end while for in to break continue return
and or not

Identifiers starting with a capital letter (A - Z) define constant variables. Once defined, constant variable values cnanot be changed.

CONST = 1
CONST = 2 # Error: Cannot assign to constant variable "CONST"

Blocks

A block is a list of statements, which are executed sequentially.

Newlines (\n) are meaningful in Luiggi, they are used to separate statements:

log("Hello")
log("World")

Luiggi strives to support multi-line statements where appropriate, such as in the middle of an unfinished expression:

log("Hello " +
    "World")

Assignment

The same syntax is used to declare and assign a value to a variable:

foo = (1 + 2) * 6 # Declares the variable foo, and assign 18 to it
foo = "Hello" + " World" # Assign "Hello World" to foo

Assignment in Luiggi is a statement. Contrary to other languages such as C, it is not possible to assign values to a variable in the middle of an expression.

Control flow

Branching statements and expressions decide whether or not to execute some code and looping ones execute something more than once.

Truth

Luiggi considers the following expression results as false:

  • The boolean value false is false.
  • The null value null is false.
  • The numeric value 0 is false.

Everything else is true, including empty strings and empty collections.

If statement

The simplest branching statement, if lets you conditionally skip a chunk of code. It looks like this:

if ready then log("Go!")

This evaluates the expression after if. If it's true, then the statement or block after the then is evaluated, otherwise it is skipped.

Instead of a single statement, you can have a block of code, in which case you must omit then and directly go to the next line:

if ready
    str = "Go!"
    log(str)
end

You may also provide one or several else if branches, and a final else branch. Each else if branch will be executed if the condition is true (and the previous ones were false). The final else branch will be executed if all previous conditions were false.

choice = 1

if choice = 0
    log("Choice 0") # Not executed
else if choice = 1
    log("Choice 1") # Executed
else
    log("Choice 2") # Not executed
end

Single-line if <expr> then <statement> constructs cannot use else.

While statement

There are two loop statements in Luiggi, and they should be familiar if you've used other imperative languages.

The simplest is the while statement. It executes a chunk of code as long as a condition continues to hold. For example:

# Hailstone sequence

n = 27
while n != 1
    log(n)

    if n % 2 = 0
        n = n / 2
    else
        n = 3 * n + 1
    end
end

For statement

The for statement exists in two form: the first iterates through a list elements, and the second one creates an index variable that is incremented from a start value (inclusive) to an end value (non-inclusive).

The first form can be used like this:

for beatle in ["george", "john", "paul", "ringo"]
    log(beatle)
end

# Prints out george, john, paul, ringo

The second form can be used like this:

animals = ["rabbit", "cat", "dog"]

for i in 0 to length(animals)
    log(i + " = " + animals[i]
end

# Prints out 0 = rabbit, 1 = cat, 2 = dog

Break and continue statements

You can use break to bail out right in the middle of a loop body. It exits from the nearest enclosing while or for loop.

for i in [1, 2, 3, 4]
    log(i)
    if i = 3 then break
end

# Prints out 1, 2, 3 (but not 4)

The continue can be used to skip the remaining loop body and move to the next iteration. Execution will immediately jump to the beginning of the next loop iteration (and check the loop condition).

for i in [1, 2, 3, 4]
    if i = 2 then continue
    log(i)
end

# Prints out 1, 3 and 4 (but not 2)

Functions

You can define a function with the following syntax:

func sum(x, y)
    return x + y
end

Once defined, the function can be called by specifying an argument for each parameter, separated by a comma.

value = sum(3, 22)
log(value) # Print out 25

The value null is returned implictly if function execution ends without a return statement.

Just like in Javascript and other languages, functions can be called before they are defined:

hello("Jack")

func hello(name)
    log("Hello " + name)
end

Values

Primitive types

Null

The null value is special, and is used to indicate the absence of a value. If you call a function that doesn't return anything, you get null back.

Booleans

A boolean value represents truth or falsehood. There are two boolean literals, true and false.

Numbers

Luiggi has a single numeric type: double-precision floating point.

12
-5678
3.14159
1.0
-12.34
Strings

A string is an array of bytes. Typically, they store characters encoded in UTF-8, but you can put any byte values in there, even zero or invalid UTF-8 sequences, even though it is probably not the best idea.

String literals are surrounded in simple or double quotes, and the following lines are equivalent:

"Foo!"
'Bar!'

Multi-line strings are explicitly not supported, in order to support context-independent line-by-line parsing.

The following escape sequences can be used in string literals:

"\n" # Newline
"\r" # Carriage return
"\t" # Tab
"\'" # A simple quote character
"\"" # A double quote character
"\\" # A backslash

Lists

Definition

A list is a compound object that holds a collection of elements identified by an integer index. You can create a list by placing a sequence of comma-separated expressions inside square brackets:

[11, "foo", false]

You can also place each element on a separate line, in which case the comma can be skipped:

[
    11, # You can use a comma
    "foo" # Or skip it if you want
    "bar"
]

The elements don't have to be of the same type.

Accessing elements

You can access an element from a list with the subscript syntax:

animals = ["rabbit", "cat", "dog", "beetle"]
log(animals[0]) # rabbit
log(animals[1]) # cat

It's a runtime error to pass an index outside of the bounds of the list. If you don't know what those bounds are, you can find out using length:

log(length(animals)) # 4

You can change an element by assigning a value to it:

animals[0] = "horse"
log(animals) # horse, cat, dog, beetle
Adding elements

You can use append to add elements to the end of an existing list:

animals = ["rabbit", "cat"]
append(animals, "dog")
log(animals) # rabbit, cat, dog
Remove elements

Use truncate to remove elements from the end of an existing list:

animals = ["rabbit", "cat", "dog"]
truncate(animals, 2)
log(animals) # rabbit

Objects

Definition

An object is a compound object that holds a collection of elements identified by an identifier. You can create an object by placing a sequence of assignments inside curly braces:

{ x = 1, y = 2 }

You can also place each element on a separate line, in which case the comma can be skipped.

{
    x = 1, # You can use a comma
    y = 2 # Or skip it if you want
    z = 3
}

Once an object is created, you cannot add or remove members.

Accessing members

You can access an element from an object by using the dot operator:

player = { name = "Niels", x = 1, y = 2 }
log(vec.name) # Niels
log(vec.x) # 1

You can change an element by assigning a value to it:

vec.name = "Luiggi"
log(vec.name) # Luiggi

Operators

The following operators are supported in expressions, by order of precedence, from loosest to tightest:

PrececedenceOperatorDescriptionTypeAssociates
1orLogical ORBinaryLeft
2andLogical ANDBinaryLeft
3notLogical NOTUnaryRight
4< <= > >=ComparisonBinaryLeft
5+ -Add, substractBinaryLeft
6* /Multiply, DivideBinaryLeft
7-NegateUnaryRight

Standard library

Console

FunctionParametersDescription
logvalueLog value to console (with newline)

Strings

FunctionParametersDescription
lengthstrReturn length of list or string
upperstrReturn string converted to uppercase
lowerstrReturn string converted to lowercase

Lists

FunctionParametersDescription
lengthlistReturn length of list
appendlist, valueAppend value to list
truncatelist, countRemove count elements from the end of list

Objects

FunctionParametersDescription
membersobjList object members
getobj, memberGet member from object
setobj, member, valueSet member in object

Functions

FunctionParametersDescription
paramsparamsList function parameters

Math

FunctionParametersDescription
minx, yReturn smallest value between x and y
maxx, yReturn biggest value between x and y
clampx, min, maxReturn x clamped between min and max
is_nanxReturn true if x is a NaN, false otherwise
floorxRound x to the next smaller integer
ceilxRound x to the next larger integer
roundxRound x to the nearest integer
absxReturn absolute value of x
expxReturn e ^ x
lnxReturn the natural logarithm of x
log2xReturn the base 2 logarithm of x
log10xReturn the base 10 logarithm of x
powx, exponentReturn x ^ power
sqrtxReturn the square root of x
cbrtxReturn the cubic root of x
cosxReturn the cosine of the specified angle (in radians)
sinxReturn the sine of the specified angle (in radians)
tanxReturn the tangent of the specified angle (in radians)
acosxReturn the arccosine (in radians) of a number
asinxReturn the arcsine (in radians) of a number
atanxReturn the arctangent (in radians) of a number
atan2x, yReturn the principal value of the arctangent (in radians) of y/x

Random

FunctionParametersDescription
randomReturn random float between 0 (included) and 1 (non-included)
random_floatmin, maxReturn random float between min (included) and max (non-included)
random_intmin, maxReturn random integer between min (included) and max (non-included)

Drawing

Get started

There are two ways: the first uses NPM and allows to run Luiggi code esily using Node.js, but will not give you the ability to produce redistribuable executable files.

The second one builds a modified Node.js binary.

With NPM (easier)

This will allow to test Luiggi quickly, and allows for faster development. But you won't be able to build self-contained redistributable binaries of your games.

Windows

First, make sure the following dependencies are met:

Once these dependencies are met, simply run the follow command:

npm install

After that, running Luiggi scripts can be done this way:

npm run luiggi examples/words/words.luiggi

Linux

Make sure the following dependencies are met:

Once these dependencies are met, simply run the follow command:

npm install

After that, running Luiggi scripts can be done this way:

npm run luiggi examples/words/words.luiggi

macOS

Make sure the following dependencies are met:

Once these dependencies are met, simply run the follow command:

npm install

After that, running Luiggi scripts can be done this way:

npm run luiggi examples/words/words.luiggi

Build modified Node.js binary (harder)

Luiggi uses a modified Node.js LTS binary that include a few additional modules, you need to build it first.

With this version, you will soon be able to build self-contained redistributable binaries of your games.

Windows

To build Node, install the following dependencies:

  • Python 3.8 or newer
  • The "Desktop development with C++" workload from Visual Studio 2022 or 2019 or the "C++ build tools" workload from the Build Tools, with the default optional components.
  • The NetWide Assembler, for OpenSSL modules. If not installed in the default location, it needs to be manually added to PATH. You can build without NASM, with option --no_asm.

Once these dependencies are met, open a command prompt in the repository and run the following command:

npm install # Only needed once
npm run build # Add `-- --no_asm` if you havne't installed and exposed NASM in path

After that, you can run the modified binary like this:

luiggi.exe src/luiggi/luiggi.js examples/mighty.luiggi

Linux

To build Node, install the following dependencies:

  • Python 3.8 or newer
  • gcc and g++ >= 8.3 or newer
  • GNU Make 3.81 or newer
  • Ninja build system

Once these dependencies are met, open a command prompt in the repository and run the following command:

npm install # Only needed once
npm run build

After that, you can run the modified binary like this:

./luiggi src/luiggi/luiggi.js examples/mighty.luiggi

macOS

  • Python 3.8 or newer
  • Xcode Command Line Tools >= 11 for macOS
  • Ninja build system

macOS users can install the Xcode Command Line Tools by running xcode-select --install. Alternatively, if you already have the full Xcode installed, you can find them under the menu Xcode -> Open Developer Tool -> More Developer Tools.... This step will install clang, clang++, and make.

Once these dependencies are met, open a command prompt in the repository and run the following command:

npm install # Only needed once
npm run build

After that, you can run the modified binary like this:

./luiggi src/luiggi/luiggi.js examples/mighty.luiggi

Examples

You can find several examples in the examples/ subdirectory, including a small game in examples/words/.

You are free to study and modify them as long as you respect the conditions of the AGPL 3.0 license.

FAQs

Package last updated on 24 Jun 2022

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc