Skip to content

Basic Syntax

Sema is a Lisp, meaning it has a very simple and uniform syntax based on S-expressions (symbolic expressions). If you have used Scheme or Clojure, you will feel right at home. If you are new to Lisp, this guide will introduce you to the core rules of the syntax.


1. S-Expressions and Prefix Notation

All code and data in Sema are represented as S-expressions. An expression is either a single value (like a number or string) or a list of expressions enclosed in parentheses.

In Sema, operators and functions always come first. This is called prefix notation:

sema
(+ 1 2)         ; => 3
(* 10 (+ 2 3))  ; => 50 (10 * (2 + 3))

Here is how to read (+ 1 2):

  1. The opening parenthesis ( starts a list.
  2. The first element + is the function or operator to call.
  3. The remaining elements 1 and 2 are the arguments passed to it.
  4. The closing parenthesis ) ends the call.

2. Comments

Comments in Sema start with a semicolon ; and run to the end of the line:

sema
; This is a single-line comment

(+ 1 2) ; This is an inline comment

By convention:

  • A single semicolon ; is used for inline comments.
  • A double semicolon ;; is used for comments on their own line.

3. Basic Types

Sema supports standard scalar types:

  • Numbers: Integers (42) and floats (3.14).
  • Strings: Double-quoted text ("hello world").
  • F-Strings: Interpolated strings prefixed with f (f"Hello ${name}").
  • Booleans: #t (true) and #f (false).
  • Nil: The null/empty value (nil).
  • Keywords: Colon-prefixed identifiers (:name, :status) commonly used as map keys or identifiers.

4. Variable Bindings

There are two primary ways to declare variables: globally and locally.

Global Bindings (define)

Use define to bind a name to a value globally:

sema
(define pi 3.14159)
(define radius 10)

(* pi (* radius radius)) ; => 314.159

Local Bindings (let)

Use let to bind variables within a specific scope. The syntax uses a list of (variable value) pairs:

sema
(let ((width 10)
      (height 5))
  (* width height)) ; => 50

;; 'width' and 'height' are not visible here

5. Core Collections

Sema supports three main collection types: Lists, Vectors, and Maps.

Lists

Lists are ordered, linked collections. Since parentheses denote code execution, you create a literal list using the list function or by quoting it with ':

sema
(list 1 2 3)    ; => (1 2 3)
'(1 2 3)        ; => (1 2 3)

Vectors

Vectors are array-like collections with fast index-based access. They are defined using square brackets []:

sema
(define my-vector [10 20 30])
(nth my-vector 1)   ; => 20 (0-indexed)

Maps

Maps are key-value structures. They are defined using curly braces {} and typically use keywords as keys:

sema
(define user {:name "Ada" :age 36})

Keyword Accessors

In Sema, keywords act as accessor functions. You can look up a value in a map by calling the keyword with the map as an argument:

sema
(:name user)    ; => "Ada"
(:age user)     ; => 36

Next Steps

Now that you know how to represent data and variables, let's learn how to organize code into functions: