Precedent Programmers Guide

Table of Contents


Preface

About This Guide

Purpose of the guide

This guide describes the Precedent programming language, the Precode low-level language, and the compiler that connects them. It is written for programmers who want to build complete software in Precedent, including performance-sensitive programs such as game engines, game projects, tools, and supporting runtime systems.

The guide has three goals. First, it defines the language clearly enough that ordinary programs can be written and maintained without relying on guesswork. Second, it explains the design principles behind Precedent so that programmers can write code that matches the language’s intended style. Third, it documents how Precedent and Precode work together within a single compiler, including where each language is most appropriate.

Precedent is a practical language. This guide therefore favors direct explanation, concrete examples, and operational rules over abstract theory. Where the language makes trade-offs, those trade-offs are described plainly so that the programmer can make informed decisions.

Intended audience

This guide is written for programmers who are comfortable with structured programming and who want a language designed around explicit control flow, predictable data layout, and practical performance.

Precedent is especially suitable for readers who already know one or more of the following:

Pascal or another Pascal-style language
C or similar systems languages
procedural game or engine programming
low-level performance-oriented programming

The language is intended to be learnable, but it is not designed around the assumption that programming should be effortless or entirely self-discovering. A minimum level of technical competence is expected. Precedent treats source code as specification: it should be readable, precise, and deliberate.

Programmers coming from object-oriented languages can use Precedent comfortably, but they should not expect class-oriented design patterns to be central to the language. Precedent provides its own solutions for organizing code and data.

How to use this guide

Readers new to Precedent should begin with the Preface and Part I, then continue through the core language chapters in order. That reading path introduces the language as it is intended to be used: first as a structured procedural language, then as a systems language with explicit data and memory rules.

Experienced programmers may prefer to use the guide as a reference. In that case, the recommended order is:

Read "What Is Precedent?"
Read "Precedent and Precode"
Skim "Intrinsic Types", "Memory Model", and "Status Values and Error Reporting"
Use the later chapters and reference appendices as needed.

Precode sections should be read after the core Precedent material unless the immediate task involves low-level module replacement, architecture-specific tuning, or mixed Precedent/Precode work.

Conventions used in examples

What Is Precedent?

Language goals and philosophy

Precedent is a procedural language designed with hindsight. Its design begins from a simple question: if compiler and language design were rolled back to the 1970s, and then reconsidered with modern implementation knowledge, what would be kept, what would be removed, and what would be redesigned?

The answer in Precedent is not nostalgia for its own sake. It is a deliberate preference for structured, procedural programming because that style produces programs whose data flow, control flow, and storage behavior are easier to reason about and easier to optimize well.

Precedent does not center its design around object-oriented features such as inheritance, virtual dispatch, or constructor-driven heap object life-cycles. Those features can be useful in some domains, but they also encourage patterns that are costly in performance-sensitive systems: fragmented allocation, pointer-heavy object graphs, unpredictable indirection, and added instruction-cache and data-cache pressure. Precedent instead favors syntax and semantics that keep the structure of a program visible to both the programmer and the compiler.

This is not a philosophical rejection of abstraction. It is a practical preference for abstractions that preserve clear data layout and explicit control. The language is designed so that more optimization can arise from the structure of the source itself, reducing the need to recover intent later through increasingly complex heuristics.

Historical influences

Precedent is strongly influenced by the work of Niklaus Wirth, both in language design and in the broader discipline of program structure.

The Pascal family provides the most visible historical influence. From Pascal, Precedent inherits the idea that a language should have a readable structure, a disciplined syntax, and a close relationship between what the programmer writes and what the program does.

A second major influence is Wirth’s view of algorithms and data structures as distinct but cooperating parts of program design. In that tradition, code and data are not treated as interchangeable concepts. Programs succeed when the points of interaction between behavior and representation are well chosen, limited, and explicit.

This separation is reflected throughout Precedent. Data is represented through strongly defined language types and intrinsic collections. Program behavior is expressed procedurally. The language encourages the programmer to be intentional about where those two meet, rather than blurring them together through object identity, inheritance hierarchies, or interface-heavy indirection.

Relationship to Pascal-style languages

Precedent belongs to the Pascal tradition in structure, readability, and program organization, but it is not a compatibility dialect of any existing Pascal compiler.

Program, unit, declaration, block, and statement structure follow a recognizably Pascal-style form. A programmer familiar with classic Pascal, early Turbo Pascal, OOP Pascal Variants, or related structured languages will find much of the surface organization familiar.

That familiarity should not be mistaken for equivalence. Precedent departs from classic Pascal in important ways:

  • Its type system is different.
  • Its memory and allocation model are different.
  • Its error-handling model is different.
  • Its low-level integration model is different.
  • It is designed alongside Precode as part of an all-in-one compiler rather than a traditional tool chain.

Precedent should therefore be approached as its own language, not merely as Pascal with extra features. Pascal is the historical foundation; Precedent is a distinct system built for a different set of practical goals.

What makes Precedent distinct

Several features distinguish Precedent from both classic Pascal-style languages and modern object-oriented systems languages.

Precedent is procedural by design, not as a limitation but as an optimization-friendly programming model. The language assumes that explicit structure is a strength.

The Precedent type system is built around practical program representation rather than around class hierarchies. Alongside intrinsic scalar and system types, the language provides structured forms such as struct, union, plex, and abstract, each serving a different role in layout, representation, and behavior.

The language treats collections as intrinsic concepts rather than as an afterthought in a runtime library. This gives the compiler semantic information about storage and usage patterns that can support stronger reasoning and safer optimization.

Precedent does not expose a general-purpose heap as the ordinary basis for program construction. Instead, it emphasizes scoped variables, collection-based allocation, managed strings, and explicit data ownership patterns suitable for systems work.

The compiler is designed as a single integrated system. Compilation, lowering, code generation, linking, hot-linking, and debugging support are not arranged as a historical chain of loosely coupled tools. They are part of one compiler with multiple modes of operation, allowing the entire build process to remain in memory and making fast incremental execution practical.

Precedent projects are designed to be self-describing at the source level, allowing the compiler to resolve dependencies, target-conditional units, and low-level project structure without requiring a separate make-style build driver.

Precedent is designed together with Precode. Because Precode is the compiler’s own intermediate language and also a user-visible writable form, the boundary between high-level source, debug visibility, library distribution, and low-level implementation remains within one coherent model.

Precedent and Precode

Overview of the two languages

The Precedent compiler works with two closely related languages: Precedent and Precode.

Precedent is the primary source language. It is used to write ordinary application, engine, gameplay, tool, and library code. It provides the visible structure of a program: declarations, procedures, functions, control flow, types, modules, and resource-aware programming.

Precode is the compiler’s intermediate language. Precedent source is lowered into Precode before native code generation and linking. In that role, Precode forms the central bridge between high-level source and final machine code.

Precode is also exposed as a writable language. It has an assembler-like syntax aligned with the same module structure used by Precedent, allowing selected modules or routines to be authored directly at the intermediate level when needed. Because Precode is an abstract machine-oriented language rather than a direct CPU assembler, it should be understood as a portable low-level representation rather than as a way to spell exact Intel or Arm instructions directly.

The relationship between the two languages is therefore not that of a “high-level language plus separate assembler.” Precedent and Precode are two views into one compiler’s program model: one intended for ordinary source development, the other for intermediate representation, low-level authoring, and compiler-facing visibility.

When to use Precedent

Use Precedent for the vast majority of program logic.

Precedent is the right choice for:

  • application structure
  • engine systems
  • gameplay code
  • tools and utilities
  • reusable units and libraries
  • code that benefits from strong readability and explicit organization
  • code where data layout matters but should still be expressed in a structured high-level form

As a general rule, write in Precedent unless there is a concrete reason to work at the Precode level. The language is intended to be sufficient for most systems and game programming tasks without forcing the programmer down into the compiler’s intermediate representation.

When to use Precode

Use Precode when work must be expressed at the compiler’s intermediate level rather than in ordinary Precedent source.

Typical reasons to use Precode include:

  • authoring selected low-level parts of the system library
  • supplying binary-distributed library components in Precode form
  • writing modules that are more naturally expressed in the compiler’s abstract machine representation
  • inspecting lowered program structure during debugging or compiler-oriented analysis
  • replacing selected implementations while preserving a higher-level module interface
  • working close to code generation without committing to a single target architecture’s assembler syntax

Precode should not be thought of as a conventional architecture-specific assembler. It is better understood as a cross-platform low-level language: close enough to native execution to express implementation details explicitly, but abstract enough to remain portable across the supported code generators.

Inline Precode

Module-level Precode substitutions

Interoperability model

Precedent and Precode interoperate within one compiler and one module model.

A program may be written entirely in Precedent. It may also use Precode for selected implementations, including cases where a module is delivered in binary Precode form rather than as readable source. This makes it possible to combine ordinary high-level development with lower-level or non-source-distributed components while preserving a unified project structure.

Because Precode is the compiler’s own intermediate language, the relationship between the two is especially direct. Precode is not an unrelated foreign language bolted onto the side of the system. It is the representation into which Precedent itself is lowered. This makes it suitable both as an implementation language for selected components and as a meaningful debugging or inspection layer when observing how source code is transformed.

The compiler as an integrated system

The Precedent compiler is a single executable with two principal modes of operation.

In command-line mode, the compiler accepts source code and project inputs and produces the final program image directly. Compilation, lowering, native code generation, and linking all occur within one in-memory process. No object files, assembler files, or external linker stages are required.

In service mode, the compiler runs as a long-lived process exposing interfaces for editor and IDE integration. In that mode, projects may be configured, updated incrementally, hot-linked in memory, executed in JIT-style fashion, and debugged through the compiler’s own service interfaces.

This design is deliberate. Historical compiler pipelines were arranged as separate tools largely because memory was scarce and expensive. Precedent does not preserve that separation merely out of tradition. It performs the full build sequence in memory in order to reduce turnaround time, simplify project execution, and support debugging and hot-link workflows that depend on retaining compiler knowledge throughout the process.

In addition to ordinary static output, the compiler supports hot link operation, also referred to as live link.

Hot linking is an incremental in-memory link mode intended for fast development iteration. As source changes are made, affected program parts may be recompiled, lowered, regenerated, and relinked without repeating a full external build sequence. The resulting in-memory image may then be executed directly in a JIT-style workflow.

This mode is particularly important for engine and game development, where rapid edit-run turnaround has practical value. In a hosted environment such as an IDE or engine editor, background recompilation and relinking can reduce the delay between changing code and running the project.

The compiler’s debugging support is designed with this model in mind. Hot-linked code can contain debug probe points, making it possible to debug code that was incrementally linked and executed from memory rather than produced as a separate static build artifact.


Part I — Getting Started

Your First Precedent Program

Smallest complete program

Program structure at a glance

Compiling and running

Reading compiler messages

The Structure of a Source File

Programs

Libraries

Units

Declarations and implementation sections

Module boundaries and visibility

Source layout conventions

Lexical Structure

Character set and source text

Whitespace

Comments

Identifiers

Keywords and reserved words

Literals

Operators and punctuation

Basic Syntax and Statements

Statement syntax

Blocks

Assignment

Procedure and function calls

Empty statements

Statement termination rules


Part II — Core Language Fundamentals

Names, Declarations, and Scope

Declaring constants, variables, procedures, and functions

Scope rules

Shadowing and name resolution

Lifetime and storage duration

Forward declarations

Intrinsic Types

Overview of the intrinsic type system

Integer types

Floating-point types

Logical and status types

Reference and system types

Text types

Type size, range, and representation

Choosing the right intrinsic type

Variables, Constants, and Initialization

Variable declarations

Constant declarations

Initial values

Default initialization behavior

Immutable vs mutable values

Expressions

Value-producing expressions

Operator precedence

Evaluation order

Implicit and explicit conversions

Numeric expressions

Boolean expressions

Pointer expressions

String and character expressions

Operators

Arithmetic operators

Comparison operators

Logical operators

Bitwise operators

Address and pointer operators


Part III — Control Flow

Conditional Execution

if

if ... else

Nested conditionals

Multi-way branching

Style guidance for conditionals

Iteration

while

repeat ... until

for

Count-controlled loops

Early exit and loop control

Common loop patterns

Case and Selection Constructs

Case-style branching

Matching integral values

Default branches

Restrictions and best practices

Program Flow and Termination

Entry points

Returning from procedures and functions

Exiting blocks and routines

Error and status returns


Part IV — Procedures, Functions, and Modular Programming

Procedures

Declaring procedures

Parameters

Local variables

Side effects and design guidelines

Functions

Declaring functions

Return values

Pure and impure functions

Function design patterns

Parameter Passing

Value parameters

Reference parameters

Pointer-based parameters

Passing strings and handles

Output parameters

Parameter design guidance

Units, Libraries, and Programs

Programs as top-level modules

Units for reusable code

Libraries and linkage

Imports and dependencies

Public vs private declarations

Organizing large codebases

Separate Compilation and Module Substitution

Compilation model

Interface stability

Replacing modules with Precode

Mixed-language module strategies

Testing modules in isolation


Part V — Data, Memory, and Systems Programming

Memory Model

Values and addresses

Stack, static, and other storage regions

Pointers and indirection

Handles and resource references

Safety considerations

Pointers

Declaring pointer values

Taking addresses

Dereferencing

Null or invalid pointers

Pointer arithmetic, if supported

Common pointer pitfalls

Handles and External Resources

What a handle represents

Lifecycle management

Validity checking

Passing handles between modules

Error handling with handles

Strings and Characters

Character values

String values

String literals

String operations

Encoding considerations

Interfacing strings with Precode

Status Values and Error Reporting

The role of status

Success and failure conventions

Propagating errors

Recoverable vs unrecoverable conditions


Part VI — Precode

Introduction to Precode

What Precode is

Why Precode exists

Relationship to machine architecture

Relationship to Precedent

Precode Syntax

Lexical rules

Instruction format

Labels

Operands

Directives and metadata

Comments and layout

Inline Precode

Embedding Precode inside Precedent

Accessing surrounding variables and values

Input/output conventions

Side effects and clobbering rules

Inline usage examples

Precode Modules

Replacing a full module with Precode

Program-level substitutions

Library-level substitutions

Unit-level substitutions

Interface compatibility requirements

Using Precode Safely

When Precode is appropriate

Readability and maintenance concerns

Debugging mixed Precedent/Precode code

Portability implications

Testing low-level routines


Part VII — Practical Programming in Precedent

Standard Program Structure

Small scripts and utilities

Structured programs

Reusable units

Large project layout

Common Programming Patterns

Input validation

Status-return patterns

Resource acquisition and release

Table-driven code

Procedural decomposition

Style and Readability

Naming conventions

Indentation and layout

Commenting practices

Procedure and function size

Writing beginner-friendly code

Defensive Programming

Checking assumptions

Handling invalid states

Preventing overflow and range errors

Pointer safety

Safer interfaces

Performance Considerations

Cost of procedure calls

Integer vs floating-point choices

String handling costs

Pointer-based optimization

When to drop into Precode


Part VIII — Testing, Debugging, and Tooling

Compiler Diagnostics

Syntax errors

Type and declaration errors

Reading and interpreting diagnostics

Debugging Precedent Programs

Tracing execution

Inspecting variables

Isolating faulty routines

Diagnosing status failures

Debugging pointer issues

Testing Strategy for Precedent Code

Unit testing procedures and functions

Module-level testing

Boundary-value testing

Type-range testing

Testing mixed Precedent/Precode implementations

Testing the Language Design

Using example programs to validate syntax

Regression suites

Syntax stress tests

Semantic edge-case tests

Building reference examples for the guide


Part IX — Reference

Reserved Words

Operators and Precedence Table

Intrinsic Types Quick Reference

Literal Forms Quick Reference

Statements Quick Reference

Procedure and Function Declaration Forms

Module Forms

Precode Quick Reference

Compiler Limits and Implementation Notes

Differences from Classic Pascal-Style Languages


Appendices

Appendix A. Complete Grammar Overview

Appendix B. Example Programs

Appendix C. Example Unit and Library Layouts

Appendix D. Example Precode Integrations

Appendix E. Error Message Catalog

Appendix F. Glossary

Appendix G. Index