Skip to content

Latest commit

 

History

86 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Quo Logo

Quo

Tests

Quo is a small, interpreted, embeddable, dynamically typed programming language with a tiny header-only C implementation.

Warning

Quo is Work-in-Progress. API is subject to change.

Building

Because Quo is tiny, it builds very fast. About 0.5 seconds for standard build and 3 seconds for release build if included all standard library modules.

To build quo CLI run:

# Standard build
./build.sh -b
# O3 optimization release build
./build.sh -b release
# Debug build with logs enabled
./build.sh -b debug

Documentation

Getting Started

File Format

Quo files use the .quo extension.

Command-line Tool

To run .quo scripts, use the quo command-line tool.

Run script:

$ quo script.quo

Quick Examples

Hello, World

print("Hello, World!")

Object-Oriented Programming

Quo doesn't have a class type, but classes can be easily created using functions as constructors which return a dictionary. Think of the returned dict as self, and because functions defined in dictionaries recieve this dictionary as first argument it's all works out. We can implement inheritance by creating explicit inherit(super_class) function.

# Create a new "class" with the given name
var class = fn(name) {
    return {
        "name": name,

        # Create function to inherit fields from other "classes"
        "inherit": fn(self, super) {
            # Get all keys from super "class" and set them on self
            if super and type(super) == "dict" {
                var keys = super.keys()
                loop (var i = 0, i < keys.len(), i += 1) {
                    var key = keys.get(i)
                    # Do not set keys that already exist on self
                    if self.has(key) continue
                    self.set(key, super.get(key))
                }
            }
        }
    }
}

# Create Animal class and add speak() method
var animal_class = fn() {
    var self = class("Animal")
    self.set("speak", fn(self, text) { print(self.name + " says \"" + text + "\"") })
    return self
}

# Create class instance
var cat = class("Cat")

# Inherit methods from Animal class
cat.inherit(animal_class())

cat.speak("meow") # Cat says "meow"

Language Reference

Comments

# This is a line comment

Keywords

Keyword Description
var Variable declaration
fn Function declaration
if Conditional statement
else Conditional statement
loop Loop statement
break Loop control statement
continue Loop control statement
return Function return statement
nil Nil literal
true Boolean literal
false Boolean literal

Basic Types

Type Description Literal
nil No value nil
bool Boolean true, false
num Integer or floating-point number 42, 3.14, 10_000_000
str String "hello"
arr Dynamic array [1, 2.3, "4"]
dict Key-value dictionary {"key": 69, "foo": "hello"}

Variables

Variables are declared using the var keyword.

var foo = 69

Because Quo is dynamically typed, the type of a variable can change.

var foo = 69
foo = "hello"

Functions

Function definitions are expressions that are assigned to variables using the fn keyword.

var add = fn(a, b) {
	return a + b
}

Scoping

Variables are scoped to the block they are declared in, surrounded by {}.

var foo = 69 # Global variable
{
	var bar = 42  # Local variable
	var foo = 420 # Shadowing of global variable `foo`
}

Control Flow

Quo has if, else, else if keywords for control flow.

var foo = 69

if foo < 42 {
	println("foo is less than 42")
} else if foo > 50 {
	println("foo is greater than 50")
} else {
	println("foo is between 42 and 50")
}

Loop

Quo has only one loop construct loop.

It works like classic C for loop.

loop (var i = 0, i < 10, i += 1) {
	println(i)
}

Infinite loop example:

loop (,true,) {
	println("infinite loop")
}

Loop also has break and continue keywords for early termination and skipping iterations.

loop (var i = 0, i < 10, i += 1) {
	if i == 5 continue
	if i == 7 break
	println(i) # 0 1 2 3 4 6 8 9
}

Operators

Quo supports the following operators:

  • Arithmetic: +, -, *, /, %
    var a = 69 + 42 - 10 * 2 / 3 % 2
    var s = "Hello" + "World" # "HelloWorld"
    var t = "foo" * 3         # "foofoofoo"
  • Grouping: ()
    var a = (69 + 42) - 10 * 2 / 3 % 2
  • Assignment: +=, -=, *=, /=, %=
    var a = 69
    a += 42 # a = a + 42
    a -= 10 # a = a - 10
    a *= 2  # a = a * 2
    a /= 3  # a = a / 3
    a %= 2  # a = a % 2
  • Comparison: ==, !=, <, >, <=, >=
    var a = 69
    var b = 42
    if a == b {
    	println("a is equal to b")
    } else if a > b {
    	println("a is greater than b")
    } else {
    	println("a is less than b")
    }
  • Logical and: and
    var a = true
    var b = false
    if a and b {
    	println("a and b are both true")
    } else {
    	println("a and b are not both true")
    }
  • Logical or: or
    var a = true
    var b = false
    if a or b {
    	println("a or b are true")
    } else {
    	println("a and b are not both true")
    }
  • Logical not: !
    var a = true
    if !a {
    	println("a is false")
    } else {
    	println("a is true")
    }

Standard Library

Built-in Functions

  • import(path): Imports a module from the given path. Returns the module object.
  • print(value, ...): Prints the values to the console, separated by spaces, adding new line at the end.
  • type(value): Get the type of the variable as string.
  • input(value, ...): Get the user input as string, printing optional prompt.

Built-in Types Methods

Methods are functions that are associated with a type and can be called on a value of that type.

They are accessed using the dot notation: value.method().

Basic types methods

  • str:
    • len(): Returns the length of the UTF-8 string.
    • get(n): Get the n-th character of the string.
    • contains(s): Returns true if the string contains the substring s.
    • strip(): Returns a new string with leading and trailing whitespace removed.
    • replace(old, new): Returns a new string with all occurrences of old replaced by new.
    • split(sep): Returns an array of strings split by the separator sep.
    • startswith(s): Returns true if the string starts with s.
    • endswith(s): Returns true if the string ends with s.
  • dict:
    • len(): Returns the number of key-value pairs in the dictionary.
    • get(key): Returns the value associated with key in the dictionary.
    • set(key, value): Sets the value associated with key in the dictionary.
    • values(): Returns an array of all values in the dictionary.
    • keys(): Returns an array of all keys in the dictionary.
  • arr:
    • len(): Returns the length of the array.
    • get(n): Get the n-th element of the array.
    • set(n, value): Sets the n-th element of the array to value.
    • push(value): Appends value to the end of the array.
    • pop(): Removes and returns the last element of the array.

Built-in Modules

quo CLI has multiple built-in modules.

They're source is in the include directory with quo-mod-*.h names. They are can be selectively disabled when Embedding in your own code.

All functions are implemented in C so they are fast and efficient.

Example usage:

var b64 = import("base64")
var encoded_string = b64.encode("Hello, World!")
print(encoded_string) # SGVsbG8sIFdvcmxkIQ==

Modules and methods:

  • base64: Encodes and decodes strings using base64.

    • encode(s): Returns the base64 encoding of s.
    • encode_url(s): Returns the base64 encoding of s in URL-safe format.
    • decode(s): Returns the decoded string of s.
    • decode_url(s): Returns the decoded string of s from URL-safe format.
  • csv: Parses and generates CSV files.

    • parse(s): Returns an array of rows parsed from string s.
    • parse_dict(s): Returns a dictionary with headers parsed from string s.
    • stringify(rows): Stringify array of arrays to CSV
    • stringify_dict(dict): Stringify array of dictionaries to CSV
  • env: Access environment variables.

    • get(key): Returns the value of the environment variable key.
    • set(key, value): Sets the value of the environment variable key to value.
    • unset(key): Removes the environment variable key.
    • all(): Returns a dictionary of all environment variables.
    • has(key): Returns true if the environment variable key exists, false otherwise.
  • dl: Dynamic loading of C libraries.

    • open(libname): Loads the C library libname and returns a QuoDLHandle.
    • QuoDLHandle: A handle to a loaded C library.
      • sym(name): Returns the QuoDLSym symbol from the library.
      • call(sym, args): Calls the QuoDLSym symbol with args and returns the result.
      • close(): Closes the handle and unloads the library.
  • json: Encodes and decodes JSON strings.

    • decode(s): Decodes JSON string and returns dict.
    • encode(obj): Returns the JSON string from dict.
  • time: Time-related functions.

    • now(): Returns the current time as a num.
    • clock(): Returns the current clock time as a num.
    • sleep(seconds): Sleeps for seconds seconds.
  • os: Operating system functions.

    • system(command): Executes the command in the operating system shell.
    • name(): Returns the name of the operating system.
  • net: Network functions.

    • get(url): Sends a GET request to url and returns the response.
    • post(url, data): Sends a POST request to url with data and returns the response.
    • put(url, data): Sends a PUT request to url with data and returns the response.
    • patch(url, data): Sends a PATCH request to url with data and returns the response.
    • delete(url): Sends a DELETE request to url and returns the response.
    • request(url, method, data, headers): Sends a custom request to url with method, data, and headers dict and returns the response.
    • encode(s): URL encodes the string s and returns the result.
    • decode(s): URL decodes the string s and returns the result.
  • fs: File system functions.

    • open(path): Opens a file at path and returns a QuoFSFile object.
      • QuoFSFile: A file object that can be used to read and write to a file.
        • read(): Reads the contents of the file and returns it as a string.
        • read_lines(): Reads the contents of the file and returns it as a array of strings.
        • write(data): Writes data string to the file.
    • exists(path): Returns true if the file at path exists, false otherwise.
    • stat(path): Returns the stat information of the file at path.
    • ls(path): Returns a list of files in the directory at path.
    • mkdir(path): Creates a directory at path.
    • rm(path): Removes the file at path.
    • rmdir(path): Removes the directory at path.
    • rename(old_path, new_path): Renames the file at old_path to new_path.
    • cp(src_path, dst_path): Copies the file at src_path to dst_path.
    • cwd(): Returns the current working directory.
    • cd(path): Changes the current working directory to path.
    • get_tmp_dir(): Returns the path of the temporary directory.
  • math: Math library

    • Constants:
      • pi: Pi (π)
      • e: Euler's number (e)
      • tau: Tau (2π)
    • Functions:
      • floor(num): Returns the largest integer less than or equal to num.
      • ceil(num): Returns the smallest integer greater than or equal to num.
      • round(num): Returns the nearest integer to num.
      • trunc(num): Returns the integer part of num.
      • abs(num): Returns the absolute value of num.
      • sqrt(num): Returns the square root of num.
      • cbrt(num): Returns the cube root of num.
      • pow(base, exp): Returns base raised to the power of exp.
      • exp(num): Returns e raised to the power of num.
      • log(num): Returns the natural logarithm of num.
      • log2(num): Returns the base-2 logarithm of num.
      • log10(num): Returns the base-10 logarithm of num.
      • sin(num): Returns the sine of num.
      • cos(num): Returns the cosine of num.
      • tan(num): Returns the tangent of num.
      • asin(num): Returns the arcsine of num.
      • acos(num): Returns the arccosine of num.
      • atan(num): Returns the arctangent of num.
      • atan2(y, x): Returns the arctangent of y/x.
      • sinh(num): Returns the hyperbolic sine of num.
      • cosh(num): Returns the hyperbolic cosine of num.
      • tanh(num): Returns the hyperbolic tangent of num.
      • min(a, b): Returns the smaller of a and b.
      • max(a, b): Returns the larger of a and b.
      • clamp(num, min, max): Returns num clamped to the range min to max.
      • random(max): Returns a random number between 0 and max.
      • random_float(max): Returns a random floating-point number between 0 and max.
      • deg_to_rad(num): Converts num from degrees to radians.
      • rad_to_deg(num): Converts num from radians to degrees.
  • uuid: UUID functions.

    • v4(): Generates a random UUID v4.
    • v7(): Generates a UUID v7 (time-ordered).
    • parse(str): Parses a UUID string and returns a dictionary with valid, version, and variant fields.
    • is_valid(str): Checks if a string is a valid UUID.

Embedding

Quo is written in header-only C, so embedding it in your own code is easy. Just copy the headers from the include directory to your project.

Quo can be used as scripting language for your game, configuration language for your program etc.

The main file is quo.h, it contains:

  • Lexer/Parser
  • Bytecode Compiler
  • Virtual Machine
  • All of the embedding functions

Quo modules are quo-mod-*.h files. Modules can be excluded if not needed.

It is very simple to embed Quo in your own code.

#define QUO_IMPLEMENTATION // Define this before including quo.h in ONE of your source files
#include "../include/quo.h"

// Include modules that you need to be available in your quo code.
#include "../include/quo-mod-base64.h"
#include "../include/quo-mod-csv.h"
#include "../include/quo-mod-dl.h"
#include "../include/quo-mod-env.h"
#include "../include/quo-mod-fs.h"
#include "../include/quo-mod-json.h"
#include "../include/quo-mod-math.h"
#include "../include/quo-mod-net.h"
#include "../include/quo-mod-os.h"
#include "../include/quo-mod-time.h"
#include "../include/quo-mod-uuid.h"

 int main() {
  const char *path = "path/to/script.quo";
  char *source = quo_read_file(path);
  if (!source) {
    fprintf(stderr, "Failed to read file: %s\n", path);
    return 1;
  }
  char *cwd = quo_dirname(path);
  QuoModule *m = quo_module_new(NULL, cwd, path, source, NULL);
  quo_dealloc(source);
  quo_dealloc(cwd);
  // If module is NULL, there was compilation error.
  if (!m) return 1;
  // Load stdlib modules
  quo_mod_base64_init(m);
  quo_mod_csv_init(m);
  quo_mod_dl_init(m);
  quo_mod_env_init(m);
  quo_mod_fs_init(m);
  quo_mod_json_init(m);
  quo_mod_math_init(m);
  quo_mod_net_init(m);
  quo_mod_os_init(m);
  quo_mod_time_init(m);
  quo_mod_uuid_init(m);
  // Run the module and get the result.
  int exit_code = 0;
  QuoVar result = quo_module_run(m);
  if (quo_var_is_err(&result)) {
    fprintf(stderr, "Runtime Error: %s\n", result.val_err);
    exit_code = 1;
  } else if (quo_var_is_num(&result)) exit_code = (int)result.val_num;
  quo_var_unref(&result);
  quo_obj_unref((QuoObj *)m);

  return exit_code;
}

To see the example of embedding Quo in your own code, see the main.c file.

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages