Precedent Programmers Guide
Table of Contents
- Preface
- About This Guide
- What Is Precedent?
- Precedent and Precode
- The compiler as an integrated system
- Hot link and service operation
- Part I — Getting Started
- Part II — Core Language Fundamentals
- Names, Declarations, and Scope
- Scope rules
- Intrinsic Types
- 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
- String-related operators
- Assignment-related forms
- 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
- Recommended error-handling patterns
- 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
- Link and module 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
- Appendices
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.
Hot link and service operation
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.