Language Engineering as a Toy Language โ the name is Small D. Integrated with the Eclipse Debug UI ๐, it provides a simple interpreter and debugger.
- Overview
- What and Why is small D?
- Blueprint
- Features of the small D Project
- small D Specification
- Class Hierarchy & Execution Flow
- Setup & Deploy
- Troubleshooting
- References
- License
org.xtext.labs.mydsl.interpreteris fully developed and designed based on parsing results. It's not a built-in feature of the Xtend framework.- The interpreter is integrated with the Eclipse Debug UI โ this is an independent module, not relying on Xtend.
- Code editor functionalities are provided and fixed by the Xtend framework.
- Minimap view and deployment are implemented via Eclipse plugin development.
- ANTLR parses the language definition file at
org.xtext.labs.mydsl/src/org/xtext/labs/Mydsl.xtext. Once parsing is complete and the AST is generated, it becomes the foundation for all language customization and engineering.
Small D is a toy language for exploring Language Engineering.
โDโ stands for DSL (Domain-Specific Language). Unlike general-purpose languages like C or Java, DSLs are tailored for specific tasks or domains.
Normally, implementing a new language doesnโt make sense for time- or budget-constrained projects. But sometimes, it's necessary. Unfortunately, simple and practical references are hard to find, and most DSLs are proprietary, making examples rare.
Even finding debugger documentation often leads to low-level gdb or obscure references โ not practical for newcomers. Thatโs why I created a small language with essential IDE features for learning purposes. I hope it helps you, too. ๐
- What: Lexer ๐ช โ Parser ๐ โ Interpreter ๐ง โ Debuggable Interpreter ๐ โ Eclipse Debug UI ๐๏ธ
- Intermediate Products: Token (lexer) โ AST (parser) โ Call Stack & Symbol Table (interpreter)
- How: Using Xtext Framework ๐ ๏ธ + Custom Implementations ๐ก
๐งฑ Xtext documentation is sparse, sometimes outdated, and the community is small โ but it's still better than nothing.
Features:
- Error Checking
- Semantic/Syntax Coloring
- Outline View
- Hover Tooltips
- Auto-completion (Proposal)
- Scoping, Cross-referencing, Labeling
- Minimap View
- Formatting, ๐ ๏ธ Quick Fix
- Folding, ๐ Go-To Declaration
- Features a ๐งฎ call stack, ๐งพ symbol table, and AST (Abstract syntax tree)
- Interpreter can suspend โธ๏ธ and resume
โถ๏ธ based on commands
- Communication with Eclipse Debug UI:
- Request Socket <=> Response (data)
- Event Socket
- Use the Eclipse Product export wizard ๐งโโ๏ธ
- Project:
org.xtext.labs.mydsl.product - File:
DSLDeveloper.product
๐ ๏ธ For installers: Use Inno Setup โ free and easy ๐ธ
- DSL code can be converted to Java and C# (though not perfectly, a feature of Xtend Framework)
- In C#, all function parameters are converted using
ref(demonstrates call-by-reference) ๐ โ ๐ก Pascal is a well-known language that supports call-by-reference. org.xtext.labs.mydsl/src/org/xtext/labs/generator/MydslGenerator.xtendโ Java generatororg.xtext.labs.mydsl/src/org/xtext/labs/generator/MydslGeneratorCS.xtendโ C# generator
Sample code can be found in org.xtext.labs.mydsl.product/src
num,string,bool- Multi-dimensional arrays
if ~ else(noelse if)whileloop only- Function definition:
def function_name() {} launch_mainis the entry point- Scope resolution: local โ global fallback
- No object-oriented features
-
Not supporting Direct function calls in expressions
โ
printstr(numtostr(b))
โa = numtostr(b) printstr(a)
| Function | Description |
|---|---|
printstr(varStr) |
๐จ๏ธ Print string |
strjoin(var1, var2) |
๐ Join two strings |
varArr = strsplit(var1, "delimiter") |
Split string into array |
varStr = numtostr(varNum) |
Convert number to string |
getargs(index) |
Get command line argument |
The interpreter module (org.xtext.labs.mydsl.interpreter) is designed with a layered architecture for processing DSL statements and managing debugging state.
classDiagram
direction TB
%% Base Thread Management
class ThreadLauncher {
<<abstract>>
#Thread t
#boolean suspended
+start()
+resume()
+suspend()
}
%% Process Handler for Debugging
class AbstractProcessHandler {
<<abstract>>
#ThreadStateForDebugging(BodyStatement)
#waitOrResumeBodyExpr(BodyStatement)
+suspend()
+resume()
}
%% Body Statement Switcher
class AbstractBodySwitcher {
+executor(BodyStatement, String)
#executeTerminalOrMethod(TerminalOrMethod, String, AbstractStackHelper)
+run()
}
%% Stack Helper
class AbstractStackHelper {
<<abstract>>
#boolean isBrk
#boolean isRtn
#Object lastFunctionReturn
#lookupStackItem(String)
#lookupSymbolByTerminal(Terminal, String)
#DecoupleTerminal(Terminal, String)
#pushCallStackItem(String)
}
%% Logical Helper
class AbstractLogicalHelper {
<<abstract>>
#evaluateLogical(boolean, String, boolean)
#evaluateCondition(Object, String, Object)
}
%% Concrete Runners
class DirectRunner {
-DSLProgram program
+run()
-execute(mainDeclared)
-execute(EList~varDeclared~)
}
class Debuggable {
-DSLProgram program
-Socket event
+run()
-execute(mainDeclared)
-execute(EList~varDeclared~)
}
%% Body Handlers
class IBody {
<<interface>>
+execute(String funcId)
}
class BodyVarExpression {
+execute(String)
}
class BodyVarDeclared {
+execute(String)
}
class BodyMethodCall {
+execute(String)
}
class BodyIf {
+execute(String)
}
class BodyWhile {
+execute(String)
}
class BodyVarReturn {
+execute(String)
}
class BodyBrk {
+execute(String)
}
%% Inheritance
ThreadLauncher <|-- AbstractProcessHandler
AbstractProcessHandler <|-- AbstractBodySwitcher
AbstractBodySwitcher <|-- DirectRunner
AbstractBodySwitcher <|-- Debuggable
AbstractStackHelper <|-- AbstractLogicalHelper
AbstractLogicalHelper <|-- BodyVarExpression
AbstractLogicalHelper <|-- BodyIf
AbstractLogicalHelper <|-- BodyWhile
AbstractStackHelper <|-- BodyVarDeclared
AbstractStackHelper <|-- BodyVarReturn
AbstractStackHelper <|-- BodyMethodCall
AbstractStackHelper <|-- BodyBrk
IBody <|.. BodyVarExpression
IBody <|.. BodyVarDeclared
IBody <|.. BodyMethodCall
IBody <|.. BodyIf
IBody <|.. BodyWhile
IBody <|.. BodyVarReturn
IBody <|.. BodyBrk
flowchart TD
subgraph Initialization[Program Setup]
A["Parse DSL File"] --> B["Create DSLProgram AST"]
B --> C["Debuggable Instance<br/>(AbstractBodySwitcher)"]
end
subgraph Execution_Control_Loop[Debugging & Execution Control]
C --> D{"Is Debugging Active?"};
D -- Yes (Debuggable) --> E["Listen on Socket<br/>(EventBroker/EventHandler)"];
D -- No (DirectRunner) --> I;
E --> F{"Received Command?"};
F -- Step/Resume --> G["AbstractProcessHandler.resume()<br/>Release Wait"];
F -- Suspend/Breakpoint Hit --> H["AbstractProcessHandler.suspend()<br/>Wait for Command"];
end
subgraph Statement_Execution[Core Interpreter Cycle]
G --> I["AbstractBodySwitcher.executor(Statement)"];
H --> I;
I --> J{"Statement Type?"};
J -- Var/Method Call --> K["Body* Handlers<br/>(AbstractStackHelper)"];
J -- If/While/Logical --> L["BodyIf/While<br/>(AbstractLogicalHelper)"];
J -- Return/Break --> M["BodyReturn/Brk<br/>(Update Stack State)"];
end
subgraph Runtime_State["State Management (AbstractStackHelper)"]
K & L & M --> N["Lookup/Update CallStack"];
N --> O["Check for Breakpoints<br/>(If suspended, go to H)"];
end
O --> I;
style C fill:#aaf,stroke:#333
style I fill:#fcc,stroke:#333
style H fill:#faa,stroke:#333
| Component | Description |
|---|---|
ThreadLauncher |
Base class for thread lifecycle management (start, suspend, resume) |
AbstractProcessHandler |
Handles debugging state transitions and breakpoint detection |
AbstractBodySwitcher |
Routes body statements to appropriate handlers based on type |
AbstractStackHelper |
Utilities for call stack and symbol table manipulation |
AbstractLogicalHelper |
Logical and comparison operation utilities |
DirectRunner |
Runs interpreter without debugging support |
Debuggable |
Runs interpreter with full debugging support |
EventBroker |
Socket server for Eclipse Debug UI communication |
EventHandler |
Processes debug commands (resume, step, suspend, breakpoints) |
- Import the project into Eclipse
- Export
org.xtext.labs.mydsl.interpreteras a Runnable JAR - Copy the JAR
- Open
DSLDeveloper.product - Use Eclipse Product Export Wizard (in Overview tab)
- Create a new DSL file from MyDsl project
- Export
debugDSL.jarfromorg.xtext.labs.mydsl.interpreter - Copy to:
D:\DSLDeveloper\debug\debugDSL.jar(example path)
Ports used:
29777,29888
If port error occurs:
cmd> netstat -ona | findstr 0.0:29777
TCP 0.0.0.0:29777 0.0.0.0:0 LISTENING 3116
cmd> taskkill /F /PID 3116๐ PID 3116 will be terminated
- Main function arguments set via Launch Configuration
- Use DSL Editor (not Xtext Editor) for debugging
- Dev environment: Xtext 2.12.0, Java SE 1.8, Eclipse Neon.3
- Minimap View: https://github.com/apauzies/eclipse-minimap-view
Variables view empty after suspend:
Workaround: switch view or reload it. Details
| Topic | Resources |
|---|---|
| Xtext | eclipse.org/Xtext โข LorenzoBettini (GitHub) โข hishidama.home (JP) |
| Language Engineering | Let's Build a Simple Interpreter |
| Eclipse Debugger | Eclipse Debugger How-to โข Vogella Tutorial โข Code and Me |
| Deploying as Product | Robert Wloch's Tutorial |
ยฉ 2017 kimtth

