Skip to content

anders-wartoft/latin

Repository files navigation

The LATIN Programming Language

LATIN is a programming language designed for educational purposes, focusing on simplicity and ease of understanding. It draws inspiration from natural languages, particularly Latin, to create a syntax that is both intuitive and expressive.

LATIN is an acronym for Latin Ain't This Insufferable Normally. We normally distinguish between LATIN the programming language and Latin the natural language by writing the former in all caps.

Key Features

  • Natural Language Syntax: LATIN uses a syntax that resembles ancient Latin language, making it easier for Latin speaking beginners to start coding.
  • Syntax Simplicity: The language is designed to minimize complexity with formatting, strange characters and other distractions, making it accessible for beginners. The programming language only uses upper case Roman letters. LATIN programs contain no spaces - word boundaries are determined by Latin morphology. Digits are not used in LATIN programs. Numbers are represented using words.
  • Educational Focus: LATIN is intended to be a stepping stone for learners to understand programming concepts before moving on to more complex languages.
  • Cross-Platform: LATIN can be run on various platforms, making it versatile for different development environments.

Getting Started

Hello World!

The traditional first program - printing "Hello World!" to the screen:

SCRIBE"Hello World!"

That's it! Just use SCRIBE (Latin for "write") followed by a string in quotes.

Output:

Hello World!

Running LATIN Programs

Save your LATIN code to a file (e.g., hello.lat) and run it with:

python3 latin.py hello.lat

You can also:

  • Use --english flag for English error messages: python3 latin.py hello.lat --english
  • Start an interactive REPL: python3 latin.py --repl or just python3 latin.py

Variables

In LATIN, variables are declared using the SIT keyword (from Latin "sit" meaning "let it be") followed by the variable name. Variable names must consist of uppercase letters only and follow the normal Latin grammar rules for nouns. Variables can hold either integers (represented as Roman numerals) or strings (text in quotes).

Latin Noun Rules: Variable names in LATIN must be valid Latin nouns, which means they must:

  • Belong to one of the five declensions (first through fifth)
  • Have a proper nominative singular form
  • Be either masculine, feminine, or neuter gender
  • Follow traditional Latin spelling conventions (no J, U, or W - use I, V instead)

Common examples by declension:

  • First declension (mostly feminine): PUELLA (girl), ROSA (rose), AQUA (water)
  • Second declension (mostly masculine/neuter): AMICUS (friend), SERVUS (slave), BELLUM (war)
  • Third declension (all genders): REX (king), CIVIS (citizen), CORPUS (body)
  • Fourth declension: MANUS (hand), GRADUS (step)
  • Fifth declension: RES (thing), DIES (day)

Latin Cases: LATIN uses the traditional Latin case system to determine the grammatical role of variables in statements:

  • Nominative - The subject case, used for:

    • The variable being declared after SIT
    • The left side of assignments (before EST)
    • The subject of comparisons (before AEQUAT)
    • Example: NUMERUS, PRIMUS, AMICUS
  • Accusative - The direct object case, used for:

    • The object being printed with SCRIBE
    • The thing being added (first argument to ADDE)
    • The thing being subtracted (first argument to DEME)
    • The right side of comparisons (after AEQUAT)
    • Example: NUMERUM, PRIMUM, AMICUM (typically -UM ending)
  • Dative - The indirect object case ("to" or "for"), used for:

    • The recipient in addition (second argument to ADDE)
    • Meaning "add X to Y" where Y is in dative
    • Example: NUMERO, PRIMO, AMICO (typically -O ending in 2nd declension)
  • Ablative - The separation case ("from" or "by means of"), used for:

    • The source in subtraction (second argument to DEME)
    • Meaning "subtract [accusative] from [ablative]"
    • The case determines the role: DEME NUMERUM (acc) PRIMO (abl) = "subtract NUMERUM from PRIMO"
    • If you swap the cases, you swap the roles: DEME PRIMUM (acc) NUMERO (abl) = "subtract PRIMUM from NUMERO"
    • Example: NUMERO, PRIMO, AMICO (same as dative in 2nd declension)
  • Genitive - The possessive case ("of")

    • Reserved for future use in LATIN
    • Example: NUMERI, PRIMI, AMICI
  • Vocative - The address case (calling someone)

    • Used for exception handling (IACE/CAPE)
    • Note: Function calls use VOCA (verb "I call") with accusative case, not vocative case
    • Example vocative forms: NUMERE, PRIME, AMICE
  • Genitive - The possessive case ("of")

    • Used for accessing fields of objects/structs
    • Syntax: FIELDGENITIVE EST VALUE to set a field
    • Syntax: SCRIBEFIELDGENITIVE to read a field
    • Example: NOMENSERVII means "name of servant" (NOMEN of SERVUS)
    • When a field is set on a variable, it automatically becomes a struct/object
    • Example genitive forms: NUMERI, SERVII, PERSONAE, DOMINI

When using variables in different contexts, they must be declined to the appropriate case (nominative, genitive, dative, accusative, ablative, or vocative) according to their grammatical function in the statement.

Example:

SITAMICUS
SITPUELLA
SITBELLVM

Parsing and Word Boundaries

LATIN programs contain no whitespace. The parser determines word boundaries using Latin morphological rules:

  • Keywords (SIT, EST, etc.) are recognized by exact pattern matching
  • Variable names are identified by valid Latin noun forms with their declension endings
  • Case endings (-us, -a, -um, -ae, -is, -em, -i, -o, -es, etc.) signal word boundaries
  • Verb conjugations follow standard Latin patterns

Parsing Strategy: The parser uses a greedy left-to-right algorithm that attempts to match the longest valid Latin word at each position. When multiple valid parsings exist, the parser:

  1. Prefers keywords over variable names
  2. Prefers previously declared variables
  3. Otherwise, the program is considered ambiguous

Ambiguous Programs: Some LATIN programs may have multiple valid interpretations. This is considered a feature, not a bug. Ambiguous programs encourage programmers to:

  • Choose clearer variable names
  • Use proper Latin vocabulary
  • Appreciate the inherent complexity of natural language parsing

Example of potential ambiguity:

SITAMO

Could be parsed as: SIT AMO (let there be "I love") or SITAMO (a hypothetical single word).

To avoid ambiguity, use well-formed Latin nouns from standard declensions.

Numbers

In LATIN, numbers are represented using roman numerals for numbers:

  • I: 1
  • V: 5
  • X: 10
  • L: 50
  • C: 100
  • D: 500
  • M: 1000
  • Combinations of these letters can be used to represent other numbers (e.g., IV for 4, IX for 9, XL for 40, etc.). Examples:
MXXIV  ; represents 1024
DCCCXC  ; represents 890

Comments

Comments in LATIN begin with a semicolon (;) and extend to the end of the line. Comments are ignored by the parser and can be used to explain code or add notes.

Example:

SITNUMERUS           ; declare a variable
NUMERUSESTXLII       ; set it to 42
SCRIBENUMERUM        ; output the value

Comments allow programmers to document their Latin grammar choices and explain the meaning of variable names without affecting program execution.

Control Structures

LATIN includes basic control structures such as conditionals and loops, using Latin keywords.

If Statement

  • SI (if) - conditional statement
  • ALITER (else) - else clause for conditionals (from Latin "aliter" meaning "otherwise")
  • AEQUAT (equals) - equality comparison
  • MAIVS (greater) - greater than comparison (from "maior")
  • MINOR (less) - less than comparison
  • FINIS (end) - marks the end of a control structure block

Syntax:

SI[nominative][comparison][value]
[statements]
FINIS

Or with else:

SI[nominative][comparison][value]
[statements if true]
ALITER
[statements if false]
FINIS

Examples:

SITNUMERUS
NUMERUSESTX
SINUMERUSAEQUATX     ; if NUMERUS equals 10
SCRIBENUMERUM        ; then print it
FINIS                ; end if

SINUMERUSMAIVSV      ; if NUMERUS > 5
SCRIBENUMERUM
FINIS

SINUMERUSMINORXX     ; if NUMERUS < 20
SCRIBENUMERUM
FINIS

Example with else:

SITNUMERUS
SITSUMMA
NUMERUSESTX
SINUMERUSAEQUATX     ; if NUMERUS == 10
SUMMAESTC            ; then SUMMA = 100
ALITER               ; else
SUMMAESTL            ; SUMMA = 50
FINIS                ; end if
SCRIBESUMMA          ; prints C (or L if condition was false)

While Loop

  • DUM (while) - loop keyword

Syntax:

DUM[nominative][comparison][value]
[statements]
FINIS

Example - countdown from 10:

SITNUMERUS
NUMERUSESTX          ; start at 10
DUMNUMERUSMAIVSI     ; while NUMERUS > 1
SCRIBENUMERUM        ; print current value
NUMERUSESTDEMENUMEROI ; subtract 1
FINIS                ; end loop

Arithmetic Operations

LATIN supports four basic arithmetic operations, all using proper Latin grammar:

  • ADDE [accusative] [dative] - Addition ("add X to Y")
  • DEME [accusative] [ablative] - Subtraction ("subtract X from Y")
  • MVLTIPLICA [accusative] [accusative] - Multiplication ("multiply")
  • DVCE [accusative] [ablative] - Integer division ("divide")

String Operations

LATIN provides comprehensive string manipulation capabilities:

  • IVNGE [accusative] [accusative] - String concatenation ("join")
  • INCIPITCVM [nominative] [ablative] - Check if string starts with substring ("begins with")
  • FINITVRCVM [nominative] [ablative] - Check if string ends with substring ("finishes with")
  • CONTINET [nominative] [accusative] - Check if string contains substring ("holds/contains")
  • INDICEDE [nominative] [ablative] - Find index position of substring ("point out from")
  • AEQUAT - String equality comparison (also works with numbers)

All string operations accept either string literals (in double quotes) or variables. When using variables, follow proper Latin case grammar:

  • INCIPITCVM/FINITVRCVM/INDICEDE use ablative for the second argument (CVM "with" and DE "from" take ablative)
  • CONTINET uses accusative for the second argument (direct object)

String operations that check conditions (INCIPITCVM, FINITVRCVM, CONTINET) return I (Roman numeral 1) for true and NIHIL (0) for false. INDICEDE returns the position as a Roman numeral, or NIHIL if not found.

Examples:

; String concatenation
SITNOMEN
SITSALVTATIO
NOMENEST"MARCVS"
SALVTATIOESTIVNGE"SALVE"NOMEN
SCRIBESALVTATIONE    ; outputs: SALVEMARCVS

; String checking
SITNOMEN
SITPRAEFIXVM
SITRESULTAT
NOMENEST"CAESAR"
PRAEFIXVMEST"CAE"

; Check if starts with "CAE" (using string literal)
RESULTATESTINCIPITCVMNOMEN"CAE"
SCRIBERESULTAT       ; outputs: I (true)

; Check if starts with prefix variable (using ablative case)
RESULTATESTINCIPITCVMNOMENPRAEFIXO
SCRIBERESULTAT       ; outputs: I (true)

; Check if ends with "SAR"
RESULTATESTFINITVRCVMNOMEN"SAR"
SCRIBERESULTAT       ; outputs: I (true)

; Check if contains "AES"
RESULTATESTCONTINETNOMEN"AES"
SCRIBERESULTAT       ; outputs: I (true)

; Find position of "SAR"
RESULTATESTINDICEDENOM EN"SAR"
SCRIBERESULTAT       ; outputs: III (position 3)

; String equality
SINOMENAEQUAT"CAESAR"
SCRIBE"Name is CAESAR"
FINIS

Arithmetic Examples

SITPRIMUS
SITSECUNDUS
SITTERTIUS

; Addition: 20 + 22 = 42
PRIMUSESTXX
SECUNDUSESTXXII
TERTIUSESTADDEPRIMUMSECUNDO
SCRIBETERTIUM        ; outputs: XLII

; Multiplication: 6 * 7 = 42
PRIMUSESTVI
SECUNDUSESTVII
TERTIUSESTMVLTIPLICAPRIMUMSECUNDUM
SCRIBETERTIUM        ; outputs: XLII

; Division: 100 / 2 = 50
PRIMUSESTC
SECUNDUSESTII
TERTIUSESTDVCEPRIMOSECUNDO
SCRIBETERTIUM        ; outputs: L

Structs and Objects

LATIN supports object-oriented programming using the genitive case ("of") for field access. Any variable can become a struct/object by setting fields on it.

Declaring Structs

All variables (both the object and field names) must be declared with SIT:

SITSERVUS    ; Declare the object
SITNOMEN     ; Declare field names
SITAETAS

Setting Fields

Use the genitive case to set field values. The syntax is: FIELDGENITIVE EST VALUE

Where:

  • FIELD is the field name in nominative case
  • GENITIVE is the genitive form of the object name
  • VALUE is the value to assign (string, number, or variable)

Example:

; NOMENSERVII = "name of servant" (NOMEN of SERVUS)
NOMENSERVIIEST"Marcus"

; For numbers, use an intermediate variable
NUMERUSESTXXX
AETASSERVIIESTNUMERUS

Reading Fields

Use the genitive case to access field values:

SCRIBENOMENSERVII    ; Print the name field of SERVUS
SCRIBEAETASSERVII    ; Print the age field of SERVUS

Complete Example

; Declare object and fields
SITSERVUS
SITNOMEN
SITAETAS
SITNUMERUS

; Set fields
NOMENSERVIIEST"Marcus"
NUMERUSESTXXX
AETASSERVIIESTNUMERUS

; Access fields
SCRIBE"Name:"
SCRIBENOMENSERVII
SCRIBE"Age:"
SCRIBEAETASSERVII

Output:

Name:
Marcus
Age:
XXX

Genitive Forms

Common genitive forms by declension:

  • Second declension (-US): SERVUS → SERVII, DOMINUS → DOMINI, NUMERUS → NUMERI
  • First declension (-A): PUELLA → PUELLAE, PERSONA → PERSONAE
  • Third declension: ERROR → ERRORIS, NOMEN → NOMINIS

Functions

LATIN supports function definitions and calls using proper Latin grammar with different cases:

  • FAC [nominative function name] [dative parameters] - Define a function
  • VOCA [accusative function name] [accusative arguments] - Call a function
  • REDDO [accusative return value] - Return from function

Function Grammar:

  • Functions are defined with nominative case ("this function is")
  • Functions are called with accusative case ("invoke this function")
  • Parameters use dative case in definition ("for these parameters")
  • Arguments use accusative case in calls ("with these values")

Automatic Declension: When you declare variables with SIT, LATIN automatically determines the proper declension pattern based on the ending:

  • -US: Second declension masculine (accusative: -UM, dative: -O)
  • -OR: Third declension (accusative: -OREM, dative: -ORI)
  • -A: First declension feminine (accusative: -AM, dative: -AE)
  • -VM/-UM: Second declension neuter (accusative: same, dative: -O)

Example:

; Define ADDITOR function (nominative) with parameters PRIMO, SECONDO (dative)
SITADDITOR
SITPRIMUS  
SITSECUNDUS
SITRESULTAT
FACADDITORPRIMOSECUNDO
RESULTATESTADDEPRIMUMSECUNDUM
REDDORESULTAT
FINIS

; Call ADDITOREM (accusative) with X, Y (accusative)
SITX
SITY
SITSUMMA
XESTX
YESTV
SUMMAESTVOCAADDITOREMXY
SCRIBESUMMA              ; outputs: XV (15)

User Input

LATIN supports reading user input with the LEGO keyword (Latin for "I read" or "I gather"):

SITNOMEN
SITNUMERUS

SCRIBE"What is your name?"
LEGONOMEN            ; reads text input

SCRIBE"Enter a number:"
LEGONUMERUS          ; reads Roman numeral

LEGO automatically detects whether the input is a Roman numeral or text:

  • If the input is a valid Roman numeral (I, V, X, L, C, D, M), it's stored as an integer
  • Otherwise, it's stored as a string
  • Strings can optionally be entered with quotes, which will be removed

Interactive REPL

LATIN includes an interactive Read-Eval-Print Loop for experimenting with the language:

# Start the REPL
python3 latin.py --repl

# Or just run without arguments
python3 latin.py

REPL commands:

  • Type LATIN code directly and press Enter
  • VALE - quit the REPL (Latin for "farewell")
  • ANGLICE - switch to English error messages
  • LATINE - switch to Latin error messages

Example REPL session:

LATIN> SITNUMERUS
LATIN> NUMERUSESTXLII
LATIN> SCRIBENUMERUM
XLII
LATIN> VALE
Vale! (Goodbye!)

Output Format

All numeric values are output as Roman numerals:

  • Zero is represented as NIHIL ("nothing" in Latin)
  • Positive integers use standard Roman numeral notation
  • Examples: XLII (42), C (100), MCMXC (1990)

Standard Library

Note: Standard library functions are planned for future releases. Currently, LATIN supports built-in operations only.

Planned features:

  • LEGO (read) - input from user
  • Additional string manipulation functions

Getting Help

Error Messages

LATIN provides error messages in both Latin (default) and English:

Latin Error Messages (Default):

  • ERRATUM: '[word]' non intellegitur - word not understood
  • ERRATUM: '[var]' non declaratur - variable not declared
  • ERRATUM: Syntax incorrecta - incorrect syntax
  • ERRATUM: Divisio per nihil - division by zero

English Error Messages (use --english flag or type ANGLICE in REPL):

  • Unknown word
  • Variable not declared
  • Invalid syntax
  • Division by zero

A larger example - ELIZA

Eliza is a classic chatbot program that simulates a conversation with a psychotherapist. Below is a simplified version of Eliza implemented in LATIN: (just admire the simplicity of the language)

SITINPUTVM
SITCONTINVA
SITRESULTAT
CONTINVAESTI
SCRIBE"ELIZA: Salve! Quid in animo est?"
SCRIBE"(Type VALE to exit)"
SCRIBE""
DUMCONTINVAAEQUATI
SCRIBE"You:"
LEGOINPUTVM
SIINPUTVMAEQUAT"VALE"
SCRIBE"ELIZA: Vale! Cura ut valeas."
CONTINVAESTNIHIL
FINIS
RESULTATESTCONTINETINPUTVM"tristis"
SIRESULTATAEQUATI
SCRIBE"ELIZA: Cur tristis es?"
FINIS
RESULTATESTCONTINETINPUTVM"felix"
SIRESULTATAEQUATI
SCRIBE"ELIZA: Gaudeo!"
FINIS
RESULTATESTCONTINETINPUTVM"mater"
SIRESULTATAEQUATI
SCRIBE"ELIZA: Dic mihi de matre tua."
FINIS
RESULTATESTCONTINETINPUTVM"pater"
SIRESULTATAEQUATI
SCRIBE"ELIZA: Quomodo pater tuus te afficit?"
FINIS
RESULTATESTINCIPITCVMINPUTVM"cur"
SIRESULTATAEQUATI
SCRIBE"ELIZA: Cur putas?"
FINIS
RESULTATESTCONTINETINPUTVM"volo"
SIRESULTATAEQUATI
SCRIBE"ELIZA: Quid accideret si id haberes?"
FINIS
SCRIBE""
FINIS

Exception Handling

LATIN provides exception handling using the vocative case - the grammatical form used for direct address. When you want to "call out" to an exception, you use the vocative case of the exception type.

Keywords

  • IACE (imperative "throw!") - Throws an exception that can be caught by a handler
  • CAPE (imperative "catch!") - Sets up an exception handler

Exception Types

  • ERROR - General error exception (vocative: ERROR)
  • EXCEPTIO - General exception (vocative: EXCEPTIO)
  • Custom exceptions can be declared using SIT and will be automatically declined

Basic Exception Handling

; Declare exception type
SITERROR

; Set up exception handler
CAPEERROR
NOTA"An error occurred!"
SCRIBE"Handling the error"
FINIS

; Throw an exception
IACEERROR"Something went wrong!"

SCRIBE"This won't execute"

Automatic Exceptions

Some operations automatically throw exceptions:

Division by Zero: When dividing by zero using DVCE, an ERROR exception is automatically thrown if a CAPEERROR handler is active:

SITERROR
SITNUMERUS
SITSUMMA

; Set up handler
CAPEERROR
NOTA"Cannot divide by zero!"
FINIS

NOTA"Attempting division..."
NUMERUSESTX
SUMMAESTNIHIL

; This throws ERROR automatically
NUMERUSESTDVCENUMERUSSUMMA

SCRIBE"This won't run"

Exception Flow

  1. CAPE registers an exception handler and skips the handler block initially
  2. Normal code execution continues
  3. When IACE is encountered (or automatic exception occurs), execution jumps to the handler
  4. Handler code executes
  5. When handler reaches FINIS, program terminates

Exception Messages

Exceptions can include optional messages:

IACEERROR"Detailed error message"

Debug and Logging

LATIN provides imperative keywords for debug output and logging:

  • AVDI (imperative "listen!") - Outputs debug information with [DEBUG] prefix
  • NOTA (imperative "note!") - Outputs log information with [LOG] prefix

Both keywords work like SCRIBE but add prefixes to distinguish different output types:

SITNUMERUS
NUMERUSESTXLII

NOTA"Program started"
AVDI"Debug: checking NUMERUS value"
AVDINUMERUM
NOTA"Program finished"

Output:

[LOG] Program started
[DEBUG] Debug: checking NUMERUS value
[DEBUG] XLII
[LOG] Program finished

Community and Resources

Join the LATIN programming community to share your projects, ask questions, and learn from others. Visit our [GitHub repository] (https://github.com/anders-wartoft/latin)

About

Latin Ain't This Insufferable Normally

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages