Add language specification document

This commit introduces the BNF for the Myc language, along with its core
semantics, data types, and evaluation logic. It also details the macro
system and provides an overview of the standard library. This document
serves as a foundational context for LLMs to understand and generate Myc
code.
This commit is contained in:
Michael Schimmel
2026-03-06 11:35:18 +01:00
parent 9ba4f795d1
commit 32a1f21463
7 changed files with 221 additions and 50 deletions
+161
View File
@@ -0,0 +1,161 @@
# Myc Language Specification & LLM Prompt Guide
This document defines the syntax (BNF) and semantics of the Myc language, a Lisp-like language designed for financial analysis with first-class abstract syntax trees (ASTs), closures, and a pipeline evaluation model. It serves as a foundational context for Large Language Models (LLMs) to understand and write idiomatic Myc code.
## 1. Syntax (BNF)
```ebnf
<program> ::= <expression>*
<expression> ::= <list>
| <vector>
| <record>
| <quote>
| <template>
| <splice>
| <placeholder>
| <atom>
<atom> ::= <integer>
| <float>
| <string>
| <keyword>
| <identifier>
| <field-accessor>
| "..."
<list> ::= "(" <special-form> ")"
| "(" <function-call> ")"
<special-form> ::= <if-form>
| <fn-form>
| <pipe-form>
| <again-form>
| <def-form>
| <assign-form>
| <do-form>
| <macro-form>
<function-call> ::= <expression> <expression>*
<if-form> ::= "if" <expression> <expression> [ <expression> ]
<fn-form> ::= "fn" <param-vector> <expression>
<param-vector> ::= "[" <pattern>* "]"
<pattern> ::= <identifier> | "[" <pattern>* "]"
<pipe-form> ::= "pipe" <expression> <expression>
<again-form> ::= "again" <expression>*
<def-form> ::= "def" <pattern> <expression>
<assign-form> ::= "assign" <expression> <expression>
<do-form> ::= "do" <expression>*
<macro-form> ::= "macro" <identifier> <param-vector> <expression>
<vector> ::= "[" <expression>* "]"
<record> ::= "{" ( <keyword> <expression> )* "}"
<quote> ::= "'" <expression>
<template> ::= "`" <expression>
<splice> ::= "~@" <expression>
<placeholder> ::= "~" <expression>
<integer> ::= [-]? [0-9]+
<float> ::= [-]? [0-9]+ "." [0-9]*
<string> ::= '"' [^"]* '"'
<keyword> ::= ":" <identifier>
<field-accessor> ::= "." <identifier>
<identifier> ::= <ident-start> <ident-char>*
```
*(Lexical Note: `<ident-start>` consists of alphabetic characters or `+-*/<=>!$%&_?.`. `<ident-char>` includes digits as well. Identifiers evaluating to a field accessor begin with a `.` followed by one or more valid identifier characters.)*
## 2. Core Semantics & Data Types
- **Integers and Floats:** Standard 64-bit numeric types.
- **Strings:** Double-quoted text (e.g., `"Hello"`).
- **Keywords:** Prefixed with a colon (e.g., `:price`, `:volume`). They are interned constants often used as record keys.
- **Identifiers:** Variable bindings or function names. The special token `...` acts as a No-Op (`Nop`) in AST manipulation.
- **Field Accessors:** Prefixed with a dot (e.g., `.close`, `.name`). Evaluates to a first-class function that extracts the named field from a record. They can be passed to higher-order functions like `map` or used directly: `(.name user)`.
## 3. Data Structures & Series
- **Vectors (Tuples):** Enclosed in square brackets `[1 2 3]`. Evaluates to a vector/tuple structure.
- **Records:** Enclosed in curly braces with keyword keys and any expression as values `{:id 101 :name "Alice"}`. They provide O(1) field access using internal memory layouts. Structural equality `(= {:a 1} {:a 1})` is `true`.
- **Series:** A core concept for financial analysis. Series are "infinite" queues with a maximum length (lookback).
- They are created via the `(series ...)` function specifying a record layout (e.g., `(series {:price :float :volume :int})`).
- **Indexing:** You access items in a series by calling it or an extracted field like a function with an integer index: `(my_series 0)`. **Crucially, index `0` represents the most recently pushed item.** Index `1` is the second most recent, and so on (lookback indexing).
## 4. Special Forms and Evaluation logic
- `(do expr...)`: Evaluates multiple expressions sequentially and returns the value of the last one.
- `(def pattern value)`: Binds a value to an identifier in the current scope. Supports deep pattern matching (destructuring) in the binding, e.g., `(def [a [b c]] [1 [2 3]])`.
- `(assign target value)`: Mutates an existing binding or field.
- `(if cond then else)`: Conditional evaluation. If `cond` is truthy, evaluates `then`, otherwise evaluates `else`. The `else` branch is optional.
- `(fn [params] body)`: Creates an anonymous closure. `params` uses a vector literal and supports destructuring natively. Example: `(fn [[x y]] (+ x y))`. Functions support lexical scoping (upvalues).
- `(again arg...)`: Triggers explicit Tail-Call Optimization (TCO), jumping back to the beginning of the closest enclosing function `(fn ...)` with new arguments. Example: `(again (- n 1) (* acc n))`. Similar to Clojure's `recur`.
- `(pipe inputs lambda)`: A specialized form designed for streaming or reactive evaluation. Takes a single expression resolving to inputs (often an array or a tuple) and passes them to the `lambda`. Heavily utilized in processing financial time series.
- `(call arg...)`: Any list where the head is not a special form evaluates the head as a function and calls it with the evaluated arguments.
## 5. Macros and Meta-Programming
The language provides a robust hygiene-aware macro system with explicit AST quotation forms:
- `(macro name [params] body)`: Defines a macro. During the compilation pass, macros run before normal execution.
- `'expr` (Quote): Returns the literal AST of `expr` without evaluating it.
- `` `expr `` (Template): Returns the AST of `expr` but allows selective evaluation inside it.
- `~expr` (Placeholder): Inside a `` ` `` template, evaluates `expr` and inserts its AST.
- `~@expr` (Splice): Inside a `` ` `` template, evaluates `expr` (must be a vector/tuple) and splices its elements directly into the enclosing list.
- `...` (Nop): Represents an empty or omitted AST node, heavily used in macro transformations like `(macro unless [c b] \`(if ~c ... ~b))`.
## 6. Standard Library (RTL) Overview
The Myc runtime environment provides a collection of built-in functions and macros:
### Core & Arithmetic
- **Math Operators:** `+`, `-`, `*`, `/`, `//` (integer division), `%` (modulo)
- **Comparison:** `=`, `<>`, `<`, `>`, `<=`, `>=`
- **Logic & Bitwise:** `and`, `or`, `xor`, `not`, `<<` (shift left), `>>` (shift right)
### Math
- **Unary:** `sqrt`, `sin`, `cos`, `tan`, `asin`, `acos`, `atan`, `exp`, `ln`, `log10`, `fract`
- **Rounding (Returns Int):** `round`, `floor`, `ceil`, `trunc`
- **Binary:** `atan2`, `pow`, `log`
- **Other:** `abs`, `min`, `max`, `make-random`
### Time & Dates
- `date`: Creates or parses a date.
- `now`: Returns the current timestamp.
### Series & Streaming
- `series`: Creates a new series with a defined layout.
- `push`: Pushes a new item into a series.
- `len`: Returns the current number of elements in a series.
- `create-random-ohlc`: Generates a mock Open-High-Low-Close data stream.
- `create-ticker`: Generates a mock ticker data stream.
### Standard Macros (Prelude)
- `(while cond body)`: Executes `body` repeatedly as long as `cond` is truthy.
## 7. Real-World Code Examples
**Records & First-Class Accessors:**
```clojure
(do
(def user {:id 101 :name "Alice" :role :admin})
(def role (.role user)) ; Evaluates to :admin
)
```
**Financial Pipelines and Series (with Indexing):**
```clojure
(do
;; Create a series with a typed layout (Struct-of-Arrays under the hood)
(def my_ticks (series {:price :float :volume :int :msg :text}))
(push my_ticks {:price 10.5 :volume 100 :msg "A"})
(push my_ticks {:price 11.2 :volume 200 :msg "B"})
(push my_ticks {:price 15.5 :volume 300 :msg "C"})
;; Extract the price series specifically
(def prices (.price my_ticks))
;; Series indexing: 0 is the newest (15.5), 1 is the previous (11.2)
(+ (prices 0) (prices 1)) ;; Output: 26.7
)
```
+14
View File
@@ -0,0 +1,14 @@
;; Output: 20
(do
(def tst
(fn [length]
(do
(def history (series :float))
(fn [val]
(do
length
))
)))
((tst 20) 5)
)
+24
View File
@@ -0,0 +1,24 @@
(do
(def SMA
(fn [length]
(do
(def history (series :float))
(push history 0)
(fn [val]
(do
(def sum (+ val (history 0)))
(push history sum)
(/ sum length)
))
)))
(def sma20 (SMA 20))
(def n 100)
(while (> n 0)
(do
(print "val=" n " sma20=" (sma20 n))
(assign n (- n 1))
))
)
+7 -1
View File
@@ -1,4 +1,4 @@
## Rust-Portierung des Delphi-AST-Compilers # Rust-Portierung des Delphi-AST-Compilers
* Dieses Repository enthält den Rust-Port des Delphi-Compilers. * Dieses Repository enthält den Rust-Port des Delphi-Compilers.
* Das Ziel ist, die alte Delphi-Codebase nach Rust zu portieren. * Das Ziel ist, die alte Delphi-Codebase nach Rust zu portieren.
@@ -68,3 +68,9 @@ Options:
--no-opt Disable optimization --no-opt Disable optimization
-h, --help Print help -h, --help Print help
-V, --version Print version -V, --version Print version
# Sprachspezifikation
* Dieses Dokument sollte aktuell gehalten werden.
@docs/BNF.md
-19
View File
@@ -1,19 +0,0 @@
;; advanced.myc
;; Hull Moving Average
;; Formula: WMA(2 * WMA(n/2) - WMA(n), sqrt(n))
(def HMA (fn [len]
(do
;; Create inner WMA instances
(def wma_half (WMA (/ len 2)))
(def wma_full (WMA len))
;; Create outer WMA instance
;; Note: sqrt is a native RTL function
(def wma_outer (WMA (sqrt len)))
(fn [val]
(do
(def v_half (wma_half val))
(def v_full (wma_full val))
(def diff (- (* 2 v_half) v_full))
(wma_outer diff))))))
-30
View File
@@ -1,30 +0,0 @@
(do
;; Simple Moving Average O(1)
(def SMA (fn [len]
(do
(def s (Series len))
(def sum 0.0)
(fn [val]
(do
(if (>= (count s) len)
(set sum (- sum (get_item s (- len 1))))
(nop))
(push s val)
(set sum (+ sum val))
(/ sum (count s)))))))
;; Weighted Moving Average
(def WMA (fn [len]
(do
(def s (Series len))
(def weight_sum (/ (* len (+ len 1)) 2))
(fn [val]
(do
(push s val)
(def current_sum 0.0)
(def i 0)
(while (< i (count s))
(do
(set current_sum (+ current_sum (* (get_item s i) (- len i))))
(set i (+ i 1))))
(/ current_sum weight_sum)))))))
+15
View File
@@ -7,6 +7,7 @@ pub fn register(env: &Environment) {
register_arithmetic(env); register_arithmetic(env);
register_comparison(env); register_comparison(env);
register_logic(env); register_logic(env);
register_io(env);
} }
fn register_constants(env: &Environment) { fn register_constants(env: &Environment) {
@@ -489,3 +490,17 @@ fn register_logic(env: &Environment) {
} }
}); });
} }
fn register_io(env: &Environment) {
let print_ty = StaticType::Function(Box::new(Signature {
params: StaticType::Any,
ret: StaticType::Void,
}));
env.register_native_fn("print", print_ty, Purity::Impure, |args| {
for arg in args {
print!("{}", arg);
}
println!();
Value::Void
});
}