We are currently working on new rules for what content should and shouldn't be allowed on this website, and are looking for feedback! See Esolang:2026 topicality proposal to view and give feedback on the current draft.
MØSS/Implementation draft
So far, MØSS hasn't been implemented, but we can give a rough framework for an interpreter. Maybe someday, the author of MØSS, User:PrySigneToFry, will actually implement the language.
Generic Framework
This interpreter should have an architecture like this: source code → lexer → token stream → parser → abstract syntax tree → semantic analyzer → symbol table → interpreter → final program result. During the execution engine running the program, the environment (scope), object model, built-in functions, I/O, and memory management are also really important parts.
Core Modules
Lexer
Input: source file string
Output: stream of tokens
Responsibilities: recognize keywords (if, while, func, etc.), operators (+, -, *, /, =, ==), literals (numbers, strings, booleans, nil), identifiers, and separators (braces, brackets, parentheses, commas and periods).
Design points: support single-line comments with #; ignore whitespace and newlines; provide position info (line number, column number) for error reporting.
Parser
Input: Token stream
Output: Abstract Syntax Tree (AST)
Responsibilities: Build AST nodes based on context-free grammar (using recursive descent or Pratt parsing).
AST Node Types (simplified):
- Program
- contains multiple statements
- Statement
- expression statements, variable assignments (=), if, while, for, return, break, continue, import, block
- Expression
- literals (Number, String, Boolean, Nil), identifiers, binary operations, unary operations, function calls, member access (.), index access ([]), new operations, object literals, list literals, list comprehensions, function definitions, this, __proto__ access, etc.
Design Points: Handle operator precedence and associativity; distinguish between l-values and r-values; support statement blocks as scopes.
Semantic Analyzer (Optional but Recommended)
Input: AST
Output: Annotated AST (or symbol table)
Responsibilities:
- Variable scope resolution (static scope)
- Check for undefined variables (can be deferred to runtime, but early checking helps with debugging)
- Prototype chain integrity check (optional)
- Detect infinite loops, etc. (simple implementation can be skipped to keep the interpreter simple)
Interpreter
Input: AST (usually a Program node)
Output: Program execution result (or side effects)
Responsibility: Traverse the AST, execute each statement, and manage the execution context.
Core algorithm: The eval function recursively traverses AST nodes and performs actions based on node type:
- Literal → return the value
- Identifier → look it up in the current environment
- Assignment → modify the variable in the environment
- Function definition → create a function object (closure)
- Function call → create a new environment, bind arguments, execute the function body
- new → create an object, set its prototype, call init (if any)
- Member access → look up object properties (traverse the prototype chain)
- List operations → indexing/slicing/comprehensions
- Control flow → jump based on conditions (if, while, for)
Execution model: tree-walking interpreter (not bytecode), simple and straightforward; can switch to bytecode VM later for better performance.
Environment and Scope
Role: Stores variable bindings and manages the scope chain.
Data Structure: Nested hash tables (dictionaries), where each environment points to its outer environment.
Operations:
- define(name, value): Creates a new binding in the current scope (used for var or assignment)
- assign(name, value): Finds and updates the variable in the current or outer scope if it exists
- lookup(name): Looks up the variable from inside out, throws an undefined error if not found
Special Environments: Global environment, function local environments (created anew with each call), block environments ({} create a new scope)
Object System (Object Model)
Core concept: All values (including primitives) are objects or can be wrapped; but for simplicity, we can distinguish between value types and reference types.
Object (MossObject):
- Property table (hash table): string -> Value
- Internal prototype pointer (__proto__)
- Special methods (init as constructor)
- Value representation (all unified as Value type):
- Primitive types: Number, String, Boolean, Nil
- Composite types: List (inherits from built-in List prototype), Object (regular object), Function (callable object), NativeFunction (built-in function)
- Prototype chain lookup: When accessing a property, if the current object doesn't have it, recursively check __proto__.
new operation:
- Create a new object and set its __proto__ to the constructor's __proto__ (the prototype)
- Call init method (if any) with the new object as this
- Return the new object
Built-in Features
Built-in Functions
- Standard I/O
- print, input, input_int, open, file.read, file.write, file.close
- List methods
- len, append, remove, sort, reverse, contains, index
- Math functions
- sqrt, sin, cos, pow, rand, random, etc.
- Type conversion
- number, floor, ceil, round, str, bool
- Object operations
- type, has_attr, get_attr, set_attr
- Runtime
- time.now, time.sleep
Because these are all built-in functions, we can link them to host functions in the interpreter language. For example, if we write the MØSS interpreter in Python, the print function can be directly connected to Python's print function, and if we write the interpreter in Java, we can connect print to System.out.println.
I/O Subsystem
- Standard input/output: Interacts with the host language (like Python's sys.stdout.write and input)
- File system: Provides File objects, wrapping open, read, write, and close operations, running in safe mode (only allows the current directory)
Memory Management
- Reference counting and cycle detection (or simple mark-and-sweep), since the language supports circular references (object prototype chains, lists referencing each other).
- To simplify implementation, you can rely on the host language's GC (like Python's); if the interpreter is written in Python, it's automatically managed; if it's written in C++, you'll have to implement it manually.
Execution Flow (using source code to result as an example)
- Read the source file (or input line by line in REPL)
- Lexical analysis → Token stream
- Syntax analysis → AST (Program)
- Create the global environment, register all built-in functions/objects
- Call the execution engine's eval(program, globalEnv):
- Execute each statement in the AST one by one
- Assignment statement → modify the environment
- Function definition → create a function object, store it in the environment
- Function call → create a new environment (closure captures outer environment)
- Object creation → create a new object, set prototype, call init
- Control flow → modify execution path (use exceptions or return values to control loops/function returns)
- Program ends, return the final value (usually nil or main's return value)
- If REPL, print the result of the expression evaluation and go back to step 2
Key Data Types (in Pseudo Code)
# Value unified type
class Value:
pass
class Number(Value):
def __init__(self, value): self.value = float(value)
class String(Value):
def __init__(self, value): self.value = str(value)
class Boolean(Value):
def __init__(self, value): self.value = bool(value)
class Nil(Value):
pass
class List(Value):
def __init__(self, elements=None):
self.elements = elements or []
class Object(Value):
def __init__(self, proto=None, props=None):
self.__proto__ = proto
self.properties = props or {}
class Function(Value):
def __init__(self, params, body, env, is_native=False):
self.params = params
self.body = body # AST node
self.closure_env = env
self.is_native = is_native
self.native_func = None # If native then point to the host function
# Environment
class Environment:
def __init__(self, outer=None):
self.store = {}
self.outer = outer
def define(self, name, value): self.store[name] = value
def assign(self, name, value):
if name in self.store: self.store[name] = value
elif self.outer: self.outer.assign(name, value)
else: raise NameError
def lookup(self, name):
if name in self.store: return self.store[name]
elif self.outer: return self.outer.lookup(name)
else: raise NameError
Error Handling and Debugging
- Use a unified exception mechanism (RuntimeError, SyntaxError) that includes location info.
- Provide friendly error messages (line number, column number, error description).
- Support try/catch extensions (optional, can be skipped at first).
- Add a debug mode: output AST or execution trace.
For example, a good eror message may be this:
Oops! The program failed to run.
At line 36, column 8, which belongs to __main__ scope
pront("Hello, World!")
~~~~~
IdentifierError: There is no such function called print.
And this is an optional try-catch block.
try {
# Here runs a code block which may happen some exceptions.
}
catch Something as alias {
# Error handling.
}
else {
# No error occured.
}
finally {
# Maybe retry, or maybe just end of this block, anyway.
}
Scalability and Portability
- Module System: Dynamically load other MØSS source files via import, with module objects being cacheable.
- FFI (Foreign Function Interface): Reserved extension interface, allowing functions to be registered from the host language.
- Cross-Platform: Interpreter core is platform-independent, with the I/O layer adapting to different operating systems (using the host language’s standard library).
Performance Considerations (Brief)
- For production-grade, you might consider compiling the AST to bytecode and implementing a VM, but it's not necessary at the framework stage.
- Cache prototype chain lookup results (optimize property access).
- Pay attention to the efficiency of common list and string operations (like concatenation with plus).
- Tail recursion optimization (optional), since recursion is part of the Turing completeness proof.
Development Roadmap Suggestions
- Phase 1: Implement lexical analysis, syntax parsing (supporting only variables, arithmetic, function calls), basic execution engine (no objects), and test small programs (Fibonacci).
- Phase 2: Add control flow (if, while), func definitions and closures, built-in print/input.
- Phase 3: Implement object system (prototype, this, new), list type and basic methods.
- Phase 4: Improve standard library (file, time, random), error handling, REPL.
- Phase 5: Optimization, debugging tools, documentation.