├── .gitignore
├── .scalafmt.conf
├── .vscode
├── extensions.json
├── launch.json
└── settings.json
├── LICENSE
├── README.md
├── _config.yml
├── build.sbt
├── docs
├── core-components.md
├── grammar.md
├── index.md
├── interpreter.md
└── transpiler.md
├── examples
├── fibonacci.while
├── hello.while
└── sum.while
├── mkdocs.yml
├── project
├── antlr4.sbt
├── assembly.sbt
├── build.properties
└── metals.sbt
├── src
├── main
│ ├── antlr4
│ │ └── whilelang
│ │ │ └── parser
│ │ │ └── Whilelang.g4
│ └── scala
│ │ └── whilelang
│ │ ├── compiler
│ │ ├── Main.scala
│ │ └── Semantics.scala
│ │ ├── interpreter
│ │ ├── Main.scala
│ │ └── Semantics.scala
│ │ ├── parser
│ │ ├── MyListener.scala
│ │ └── Syntax.scala
│ │ └── util
│ │ ├── ContextValue.scala
│ │ ├── Runner.scala
│ │ └── Walker.scala
└── plugins.sbt
└── while
/.gitignore:
--------------------------------------------------------------------------------
1 | *.class
2 | *.log
3 |
4 | # sbt specific
5 | .cache
6 | .history
7 | .lib/
8 | .bsp/
9 | dist/*
10 | target/
11 | lib_managed/
12 | src_managed/
13 | project/boot/
14 | project/plugins/project/
15 |
16 | # Scala-IDE specific
17 | .scala_dependencies
18 | .worksheet
19 |
20 | # Metals
21 | *.metals/
22 | project/project/metals.sbt
23 | project/project/project/metals.sbt
24 |
25 | .bloop/
26 | *.interp
27 | *.antlr
28 |
--------------------------------------------------------------------------------
/.scalafmt.conf:
--------------------------------------------------------------------------------
1 | version = "3.9.4"
2 | runner.dialect = scala3
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "itryapitsin.sbt",
4 | "mike-lischke.vscode-antlr4",
5 | "scala-lang.scala"
6 | ]
7 | }
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use o IntelliSense para saber mais sobre os atributos possíveis.
3 | // Focalizar para exibir as descrições dos atributos existentes.
4 | // Para obter mais informações, acesse: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": []
7 | }
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "files.watcherExclude": {
3 | "**/target": true
4 | }
5 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Leonardo Lucena
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # While Language
2 |
3 | [](https://app.codacy.com/gh/lrlucena/whilelang)
4 | > A minimalistic programming language built using [Scala 3.6](https://scala-lang.org) and [ANTLR 4.13](https://antlr.org).
5 |
6 | This simple programming language features a single loop construct (`while`) and supports only integer types.
7 |
8 | The language is implemented in two distinct ways:
9 | - As an [interpreter](interpreter.md)
10 | - As a [transpiler](transpiler.md) (source-to-source compiler) targeting Scala.
11 |
12 |
13 |
14 |
15 | |
16 | Interpreter |
17 | Transpiler (Compiler) |
18 |
19 |
20 |
21 |
22 | Grammar |
23 |
24 | Grammar (29 lines)
25 | |
26 |
27 |
28 | Abstract Syntax |
29 |
30 | Abstract Syntax (23 lines)
31 | |
32 |
33 |
34 | Semantics |
35 | Semantics (35 lines) |
36 | Semantics (34 lines) |
37 |
38 |
39 | Parser Rules |
40 |
41 | Listener (56 lines)
42 | |
43 |
44 |
45 | Main |
46 | Main (4 lines) |
47 | Main (4 lines) |
48 |
49 |
50 | Utility Classes |
51 |
52 | Walker (20 lines)
53 | Runner (14 lines)
54 | ContextValue (13 lines)
55 | |
56 |
57 |
58 | Total |
59 | 194 lines |
60 | 193 lines |
61 |
62 |
63 |
64 |
65 | ## Examples
66 | Below are some example programs:
67 |
68 | ### Hello World
69 | ````text
70 | print "Hello World"
71 | ````
72 |
73 | ### Sum of Two Numbers
74 | ````text
75 | print "Enter the first number:";
76 | a := read;
77 | print "Enter the second number:";
78 | b := read;
79 | sum := a + b;
80 | print "The sum is: ";
81 | print sum
82 | ````
83 |
84 | ### Fibonacci Sequence
85 | ````text
86 | print "Fibonacci Sequence";
87 | a := 0;
88 | b := 1;
89 | while b <= 1000000 do {
90 | print b;
91 | b := a + b;
92 | a := b - a
93 | }
94 | ````
95 |
96 | ---
97 |
98 | ## Compiling and Running
99 |
100 | To compile the project, you'll need to install [sbt](https://www.scala-sbt.org/). The easiest installation method is via [SDKMAN!](https://sdkman.io/install) (Linux) or [Scoop](https://scoop.sh/) (Windows).
101 |
102 | ````shell
103 | $ sbt
104 | sbt> clean
105 | sbt> compile
106 |
107 | # Run the interpreter
108 | sbt> runMain whilelang.interpreter.main sum.while
109 |
110 | # Run the transpiler
111 | sbt> runMain whilelang.compiler.main sum.while
112 | ````
113 |
--------------------------------------------------------------------------------
/_config.yml:
--------------------------------------------------------------------------------
1 | show_downloads: true
2 | theme: jekyll-theme-dinky
3 |
4 | gems:
5 | - jekyll-sitemap
6 |
--------------------------------------------------------------------------------
/build.sbt:
--------------------------------------------------------------------------------
1 | ThisBuild / scalaVersion := "3.6.4"
2 | ThisBuild / organization := "com.github.lrlucena"
3 | ThisBuild / version := "1.6.4"
4 |
5 | enablePlugins(Antlr4Plugin)
6 |
7 | Compile / packageBin / mainClass := Some("whilelang.interpreter.main")
8 |
9 | Compile / scalacOptions ++= Seq("-deprecation","-explain")
10 |
11 | assembly / mainClass := Some("whilelang.interpreter.main")
12 |
13 | lazy val hello = (project in file(".")).settings(name := "WhileLang" )
14 |
15 | libraryDependencies ++= Seq(
16 | "org.antlr" % "antlr4" % "4.13.2"
17 | )
18 |
19 | Antlr4 / antlr4Version:= "4.13.2"
20 | Antlr4 / antlr4PackageName := Some("whilelang.parser")
21 | Antlr4 / antlr4GenListener := true
22 | Antlr4 / antlr4GenVisitor := false
23 |
--------------------------------------------------------------------------------
/docs/core-components.md:
--------------------------------------------------------------------------------
1 | # Core Language Components
2 |
3 | These components are shared between both the interpreter and transpiler implementations.
4 |
5 | ---
6 |
7 | ## Language Grammar
8 |
9 | The foundation of the interpreter is defined through this ANTLR grammar:
10 |
11 | ````antlr
12 | grammar Whilelang;
13 |
14 | /* Core program structure */
15 | program : seqStatement; // Entry point for programs
16 |
17 | /* Statement sequencing */
18 | seqStatement: statement (';' statement)* ; // Semicolon-separated statements
19 |
20 | /* Statement definitions */
21 | statement: ID ':=' expression # attrib // Variable assignment
22 | | 'skip' # skip // No-op
23 | | 'if' bool 'then' statement 'else' statement # if // Conditional
24 | | 'while' bool 'do' statement # while // Loop
25 | | 'print' Text # print // String output
26 | | 'write' expression # write // Expression output
27 | | '{' seqStatement '}' # block // Statement grouping
28 | ;
29 |
30 | /* Expression hierarchy */
31 | expression: INT # int // Literal
32 | | 'read' # read // Input
33 | | ID # id // Variable
34 | | expression '*' expression # binOp // Multiplication
35 | | expression ('+'|'-') expression # binOp // Add/Sub
36 | | '(' expression ')' # expParen// Precedence
37 | ;
38 |
39 | /* Boolean expressions */
40 | bool: ('true'|'false') # boolean // Literals
41 | | expression '=' expression # relOp // Equality
42 | | expression '<=' expression # relOp // Comparison
43 | | 'not' bool # not // Negation
44 | | bool 'and' bool # and // Conjunction
45 | | '(' bool ')' # boolParen
46 | ;
47 |
48 | // Lexer rules
49 | INT: ('0'..'9')+ ; // Integer literals
50 | ID: ('a'..'z')+; // Identifiers
51 | Text: '"' .*? '"'; // String literals
52 | Space: [ \t\n\r] -> skip; // Whitespace handling
53 | ````
54 |
55 | Key features:
56 |
57 | - Simple expression hierarchy with basic arithmetic operations
58 | - Classic control structures (if/else, while)
59 | - I/O operations through read/write statements
60 | - Block scoping with curly braces
61 |
62 | ---
63 |
64 | ## Abstract Syntax Tree (AST)
65 |
66 | The AST is defined using Scala 3 enums for type-safe tree construction:
67 |
68 | ```scala linenums="1"
69 | --8<--
70 | src/main/scala/whilelang/parser/Syntax.scala
71 | --8<--
72 | ```
73 |
74 | ````scala
75 | package whilelang.parser
76 |
77 | // Statement hierarchy
78 | enum Statement:
79 | case Skip // No operation
80 | case If(condition: Bool, thenSmt: Statement, elseSmt: Statement)
81 | case Write(exp: Expression) // Output expression value
82 | case While(condition: Bool, doSmt: Statement)
83 | case Print(text: String) // Output literal string
84 | case SeqStatement(statements: List[Statement]) // Statement sequence
85 | case Attrib(id: String, exp: Expression) // Variable assignment
86 | case Program(statements: SeqStatement) // Root node
87 |
88 | // Expression types
89 | enum Expression:
90 | case Read // Input operation
91 | case Id(id: String) // Variable reference
92 | case Integer(exp: Int) // Integer literal
93 | case ExpSum(lhs: Expression, rhs: Expression)
94 | case ExpSub(lhs: Expression, rhs: Expression)
95 | case ExpMult(lhs: Expression, rhs: Expression)
96 |
97 | // Boolean expressions
98 | enum Bool:
99 | case Boole(b: Boolean) // Boolean literal
100 | case ExpEq(lhs: Expression, rhs: Expression)
101 | case ExpLe(lhs: Expression, rhs: Expression)
102 | case Not(b: Bool) // Logical negation
103 | case And(lhs: Bool, rhs: Bool) // Logical conjunction
104 | ````
105 |
106 | The AST provides:
107 |
108 | - Type-safe representation of program structure
109 | - Pattern-matchable cases for semantic analysis
110 | - Clear separation of statements, expressions, and boolean logic
111 |
112 | ---
113 |
114 | ## Parser Implementation
115 |
116 | Shared ANTLR listener that builds the AST:
117 |
118 | ```scala linenums="1"
119 | --8<--
120 | src/main/scala/whilelang/parser/MyListener.scala
121 | --8<--
122 | ```
123 |
124 | ````scala
125 | package whilelang.parser
126 |
127 | class MyListener extends WhilelangBaseListener with ContextValue:
128 | var program: Program = _ // Root AST node
129 |
130 | // Build AST nodes from parse tree events
131 | override def exitProgram(ctx: ProgramContext) =
132 | ctx.value = Program(ctx.seqStatement.value)
133 | program = ctx.value
134 |
135 | override def exitSeqStatement(ctx: SeqStatementContext) =
136 | ctx.value = SeqStatement(ctx.statement.asScala.toList.map(_.value[Statement]))
137 |
138 | override def exitAttrib(ctx: AttribContext) =
139 | ctx.value = Attrib(ctx.ID.text, ctx.expression.value)
140 |
141 | // Additional listener methods handle other node types...
142 | ````
143 |
144 | Parser features:
145 |
146 | - Inherits from ANTLR-generated WhilelangBaseListener
147 | - Uses ContextValue trait for tree property management
148 | - Constructs typed AST nodes during parse tree walk
149 | - Handles operator precedence through nested listener calls
150 |
151 |
152 | ---
153 |
154 | ## Shared Utilities
155 |
156 | ### ContextValue Trait
157 |
158 | Provides tree property management:
159 |
160 |
161 | ```scala linenums="1"
162 | --8<--
163 | src/main/scala/whilelang/util/ContextValue.scala
164 | --8<--
165 | ```
166 |
167 | ````scala
168 | package whilelang.util
169 |
170 | trait ContextValue:
171 | given ParseTreeProperty[Any] = ParseTreeProperty[Any]()
172 |
173 | extension (tree: ParseTree)(using values: ParseTreeProperty[Any])
174 | def value[E]: E = values.get(tree).asInstanceOf[E]
175 | def value_=(v: Any) = values.put(tree, v)
176 | ````
177 |
178 | Purpose:
179 |
180 | - Stores intermediate values during parse tree construction
181 | - Enables type-safe value retrieval
182 | - Facilitates AST node creation
183 |
184 | ### Walker Component
185 |
186 | Manages parsing workflow:
187 |
188 | ```scala linenums="1"
189 | --8<--
190 | src/main/scala/whilelang/util/Walker.scala
191 | --8<--
192 | ```
193 |
194 | ````scala
195 | package whilelang.util
196 |
197 | object Walker:
198 | // ANTLR configuration with error listener
199 | private def addListener(r: Recognizer[?, ?]*): Unit =
200 | r.map: r =>
201 | r.removeErrorListeners()
202 | r.addErrorListener(ThrowingErrorListener)
203 |
204 | // Parse tree construction
205 | def walk(source: String)(using listener: MyListener) = Try:
206 | val lexer = WhilelangLexer(CharStreams.fromString(source))
207 | val parser = WhilelangParser(CommonTokenStream(lexer))
208 | addListener(lexer, parser)
209 | ParseTreeWalker().walk(listener, parser.program)
210 | listener.program
211 | ````
212 |
213 | Features:
214 |
215 | - ANTLR stream management
216 | - Error listener integration
217 | - Parse tree walking
218 |
219 | ### Runner Class
220 |
221 | Handles file processing and error management:
222 |
223 | ```scala linenums="1"
224 | --8<--
225 | src/main/scala/whilelang/util/Runner.scala
226 | --8<--
227 | ```
228 |
229 | ````scala
230 | package whilelang.util
231 |
232 | object Runner:
233 | def apply(action: Program => Unit)(file: String) =
234 | sourceCode(file).flatMap(walk) match
235 | case Success(program) => action(program) // Execute valid program
236 | case Failure(e: FileNotFoundException) => println("File not found")
237 | case Failure(e) => println("Error: " + e.printStackTrace())
238 | ````
239 |
240 | Responsibilities:
241 |
242 | - File I/O operations
243 | - Error handling
244 | - Pipeline coordination
245 |
--------------------------------------------------------------------------------
/docs/grammar.md:
--------------------------------------------------------------------------------
1 | # Grammar
2 |
3 | ```antlr linenums="1" title="Whilelang.g4"
4 | --8<--
5 | src/main/antlr4/whilelang/parser/Whilelang.g4
6 | --8<--
7 | ```
8 |
9 | ```antlr
10 | grammar Whilelang;
11 |
12 | program : seqStatement;
13 |
14 | seqStatement: statement (';' statement)* ;
15 |
16 | statement: ID ':=' expression # attrib
17 | | 'skip' # skip
18 | | 'if' bool 'then' statement 'else' statement # if
19 | | 'while' bool 'do' statement # while
20 | | 'print' Text # print
21 | | 'print' expression # write
22 | | '{' seqStatement '}' # block
23 | ;
24 |
25 | expression: INT # int
26 | | 'read' # read
27 | | ID # id
28 | | expression '*' expression # binOp
29 | | expression ('+'|'-') expression # binOp
30 | | '(' expression ')' # expParen
31 | ;
32 |
33 | bool: ('true'|'false') # boolean
34 | | expression '=' expression # relOp
35 | | expression '<=' expression # relOp
36 | | 'not' bool # not
37 | | bool 'and' bool # and
38 | | '(' bool ')' # boolParen
39 | ;
40 |
41 | INT: ('0'..'9')+ ;
42 | ID: ('a'..'z')+;
43 | Text: '"' .*? '"';
44 | Space: [ \t\n\r] -> skip;
45 | ```
46 |
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | # WhileLang
2 |
3 | [](https://app.codacy.com/gh/lrlucena/whilelang)
4 |
5 | A minimalistic programming language built using [Scala 3.6](https://scala-lang.org) and [ANTLR 4.13](https://antlr.org).
6 |
7 | ## Features
8 |
9 | - Single loop construct (`while`)
10 | - Integer types only
11 | - Two implementations:
12 | - [Interpreter](interpreter.md)
13 | - [Transpiler](transpiler.md) to Scala
14 |
15 | ## Implementation Comparison
16 |
17 |
18 |
19 |
20 | |
21 | Interpreter |
22 | Transpiler (Compiler) |
23 |
24 |
25 |
26 |
27 | Grammar |
28 |
29 | Grammar
30 | |
31 |
32 |
33 | Abstract Syntax |
34 |
35 | Abstract Syntax
36 | |
37 |
38 |
39 | Semantics |
40 | Semantics (35 lines) |
41 | Semantics (34 lines) |
42 |
43 |
44 | Parser Rules |
45 |
46 | Listener (56 lines)
47 | |
48 |
49 |
50 | Main |
51 | Main (4 lines) |
52 | Main (4 lines) |
53 |
54 |
55 | Utility Classes |
56 |
57 | Walker (20 lines)
58 | Runner (14 lines)
59 | ContextValue (13 lines)
60 | |
61 |
62 |
63 | Total |
64 | 194 lines |
65 | 193 lines |
66 |
67 |
68 |
69 |
70 | ## Example Programs
71 |
72 | ### Hello World
73 |
74 | ```python linenums="1" title="hello.while"
75 | print "Hello World"
76 | ```
77 |
78 | ### Sum of Two Numbers
79 |
80 | ```python linenums="1" title="sum.while"
81 | print "Enter the first number:";
82 | a := read;
83 | print "Enter the second number:";
84 | b := read;
85 | sum := a + b;
86 | print "The sum is: ";
87 | print sum
88 | ```
89 |
90 | ### Fibonacci Sequence
91 |
92 | ```python linenums="1" title="examples/fibonacci.while"
93 | print "Fibonacci Sequence";
94 | a := 0;
95 | b := 1;
96 | while b <= 1000000 do {
97 | print b;
98 | b := a + b;
99 | a := b - a
100 | }
101 | ```
102 |
103 | ## Getting Started
104 |
105 | ### Prerequisites
106 |
107 | - [sbt](https://www.scala-sbt.org/)
108 | - [Java JDK](https://adoptium.net/) (version 11 or higher recommended)
109 |
110 | ### Installation
111 |
112 | === "Linux/macOS"
113 | ```sh
114 | curl -s "https://get.sdkman.io" | bash
115 | sdk install sbt
116 | ```
117 |
118 | === "Windows"
119 | ```powershell
120 | scoop install sbt
121 | ```
122 |
123 | ### Building and Running
124 |
125 | 1. Clone the repository:
126 | ```sh
127 | git clone https://github.com/lrlucena/whilelang.git
128 | cd whilelang
129 | ```
130 |
131 | 2. Run the interpreter:
132 | ```sh
133 | sbt "runMain whilelang.interpreter.main examples/sum.while"
134 | ```
135 |
136 | 3. Run the transpiler:
137 | ```sh
138 | sbt "runMain whilelang.compiler.main examples/fib.while"
139 | ```
140 |
141 | ## Project Structure
142 |
143 | ```tree
144 | whilelang/
145 | ├── src/
146 | │ └── main/
147 | │ ├── scala/whilelang/
148 | │ │ ├── compiler/ # Transpiler implementation
149 | │ │ ├── interpreter/ # Interpreter implementation
150 | │ │ └── parser/ # Parser files
151 | | └── antlr4/whilelang/
152 | | └── parser/ # ANTLR grammar
153 | ├── examples/ # Sample While programs
154 | └── project/ # sbt build files
155 | ```
156 |
--------------------------------------------------------------------------------
/docs/interpreter.md:
--------------------------------------------------------------------------------
1 | # While Language Interpreter
2 |
3 | [View Core Components](core-components.md)
4 |
5 | This component directly executes Whilelang programs through tree walking interpretation. The interpreter implements operational semantics using Scala's evaluation capabilities while maintaining program state in a mutable environment.
6 |
7 | ---
8 |
9 | ## Running the Interpreter
10 |
11 | Execute programs using sbt with:
12 |
13 | ````shell
14 | $ sbt
15 |
16 | # Run on a Whilelang source file
17 | sbt> runMain whilelang.interpreter.main sum.while
18 | ````
19 |
20 | ---
21 |
22 | ## Execution Semantics
23 |
24 | The interpreter implements operational semantics through extension methods:
25 |
26 | ```scala linenums="1"
27 | --8<--
28 | src/main/scala/whilelang/interpreter/Semantics.scala
29 | --8<--
30 | ```
31 |
32 | ````scala
33 | package whilelang.interpreter
34 |
35 | import scala.collection.mutable.Map
36 |
37 | // Execution environment (mutable state)
38 | type Environment = Map[String, Int]
39 | given Environment = Map[String, Int]()
40 |
41 | // Statement execution logic
42 | extension (stmt: Statement)(using env: Environment)
43 | def execute: Unit = stmt match
44 | case If(cond, tSmt, eSmt) => (if cond.value then tSmt else eSmt).execute
45 | case Write(exp) => println(exp.value) // Output expression
46 | case While(cond, doSmt) => while cond.value do doSmt.execute // Loop
47 | case Print(text) => println(text) // Output text
48 | case SeqStatement(stmts) => stmts.foreach(_.execute)// Sequence
49 | case Attrib(id, exp) => env += id -> exp.value // Assignment
50 | case Program(seq) => seq.execute // Program entry
51 | case Skip | _ => // No-op
52 |
53 | // Expression evaluation
54 | extension(exp: Expression)(using env: Environment)
55 | def value: Int = exp match
56 | case Read => io.StdIn.readInt() // Input
57 | case Id(id) => env.getOrElseUpdate(id, 0) // Variable lookup
58 | case Integer(value) => value // Literal
59 | case ExpSum(lhs, rhs) => lhs.value + rhs.value // Addition
60 | case ExpSub(lhs, rhs) => lhs.value - rhs.value // Subtraction
61 | case ExpMult(lhs, rhs) => lhs.value * rhs.value // Multiplication
62 |
63 | // Boolean evaluation
64 | extension(exp: Bool)(using env: Environment)
65 | def value: Boolean = exp match
66 | case Boole(b) => b // Literal
67 | case ExpEq(lhs, rhs) => lhs.value == rhs.value // Equality
68 | case ExpLe(lhs, rhs) => lhs.value <= rhs.value // Comparison
69 | case Not(b) => !b.value // Negation
70 | case And(lhs, rhs) => lhs.value && rhs.value // Conjunction
71 | ````
72 |
73 | Key characteristics:
74 |
75 | - Mutable environment for variable storage
76 | - Strict evaluation semantics
77 | - Direct mapping from AST nodes to execution logic
78 | - Recursive tree walking through pattern matching
79 |
80 | ---
81 |
82 | ## Main Entry Point
83 |
84 | Coordinates the interpretation pipeline:
85 |
86 | ```scala linenums="1"
87 | --8<--
88 | src/main/scala/whilelang/interpreter/Main.scala
89 | --8<--
90 | ```
91 |
92 |
93 | ````scala
94 | package whilelang.interpreter
95 |
96 | import whilelang.util.Runner
97 |
98 | // Execution entry point
99 | @main def main(file: String) =
100 | Runner(program => program.execute)(file)
101 | ````
102 |
103 | Execution flow:
104 |
105 | 1. Parses input file using shared [Walker component](core-components.md#walker)
106 | 2. Builds AST via shared [parser implementation](core-components.md#parser-implementation)
107 | 3. Executes program using interpreter semantics
108 | 4. Maintains runtime environment during execution
109 |
110 | ---
111 |
112 | ## Differences from Transpiler
113 |
114 | While sharing the [core components](core-components.md), the interpreter:
115 |
116 | 1. Directly evaluates AST nodes instead of generating code
117 | 2. Maintains runtime state rather than tracking variables for declaration
118 | 3. Uses native Scala I/O operations instead of generating I/O code
119 | 4. Implements control flow through Scala's native constructs
120 |
--------------------------------------------------------------------------------
/docs/transpiler.md:
--------------------------------------------------------------------------------
1 | # While Language Transpiler
2 |
3 | This component translates Whilelang programs into equivalent Scala code, enabling execution on the JVM through source-to-source compilation. The transpiler maintains the original program semantics while leveraging Scala's syntax and runtime environment.
4 |
5 | ---
6 |
7 | ## Transpilation Process
8 |
9 | Execute the transpiler using sbt with:
10 |
11 | ````shell
12 | $ sbt
13 |
14 | # Convert Whilelang program to Scala
15 | sbt> runMain whilelang.compiler.Main sum.while
16 | ````
17 |
18 | This generates Scala code that can be compiled and executed using standard Scala tools.
19 |
20 | ---
21 |
22 | ## Code Comparison Examples
23 |
24 |
25 |
26 |
27 | Program |
28 | Whilelang |
29 | Generated Scala |
30 |
31 |
32 |
33 |
34 | Hello World |
35 |
36 | ```ruby
37 | print "Hello World"
38 | ```
39 | |
40 |
41 | ```scala
42 | @main def main() =
43 | println("Hello World")
44 | ```
45 | |
46 |
47 |
48 |
49 | Sum of Numbers |
50 |
51 | ```ruby
52 | print "Enter the first number:";
53 | a := read;
54 | print "Enter the second number:";
55 | b := read;
56 | sum := a + b;
57 | print "The sum is:";
58 | write sum
59 | ```
60 | |
61 |
62 | ```scala
63 | @main def main() =
64 | var a, b, sum = 0
65 | println("Enter the first number:")
66 | a = readInt()
67 | println("Enter the second number:")
68 | b = readInt()
69 | sum = a + b
70 | println("The sum is:")
71 | println(sum)
72 | ```
73 | |
74 |
75 |
76 |
77 | Fibonacci Sequence |
78 |
79 | ```ruby
80 | print "Fibonacci Sequence";
81 | a := 0;
82 | b := 1;
83 | while b <= 1000000 do {
84 | write b;
85 | b := a + b;
86 | a := b - a
87 | }
88 | ```
89 | |
90 |
91 | ```scala
92 | @main def main() =
93 | var a, b = 0
94 | println("Fibonacci Sequence")
95 | a = 0
96 | b = 1
97 | while b <= 1000000 do
98 | println(b)
99 | b = a + b
100 | a = b - a
101 | ```
102 | |
103 |
104 |
105 |
106 |
107 | Key translation patterns:
108 |
109 | - `print` statements become `println` with string literals
110 | - `read` operations map to `readInt()` calls
111 | - While loops preserve their structure
112 | - Variable declarations are hoisted with default zero values
113 |
114 | ---
115 |
116 | ## Translation Semantics
117 |
118 | The core translation logic uses extension methods to convert AST nodes to Scala code:
119 |
120 | ```scala linenums="1"
121 | --8<--
122 | src/main/scala/whilelang/compiler/Semantics.scala
123 | --8<--
124 | ```
125 |
126 | ````scala
127 | package whilelang.compiler
128 |
129 | type Ids = Set[String] // Track declared variables
130 |
131 | extension (src: Any)(using ids: Ids)
132 | def meaning: String = src match
133 | case If(cond, tSmt, eSmt) =>
134 | s"if ${cond.m} then\n ${tSmt.m}\nelse\n ${eSmt.m}"
135 | case Attrib(id, exp) =>
136 | ids.add(id); s"$id = ${exp.m}" // Track variable declarations
137 | case Program(seq) =>
138 | s"@main def main() =\n ${vars(ids)}\n ${seq.m}"
139 | // Other cases handle remaining node types...
140 | ````
141 |
142 | Notable features:
143 |
144 | - **Variable Tracking**: Automatically detects variable declarations
145 | - **String Building**: Recursively constructs Scala source strings
146 | - **Syntax Preservation**: Maintains original program structure where possible
147 | - **Type Safety**: Uses pattern matching on sealed AST types
148 |
149 | ---
150 |
151 | ## Parser Implementation
152 |
153 | Shared with the interpreter, the ANTLR listener constructs the AST:
154 |
155 | ````scala
156 | class MyListener extends WhilelangBaseListener with ContextValue:
157 | override def exitAttrib(ctx: AttribContext) =
158 | ctx.value = Attrib(ctx.ID.text, ctx.expression.value)
159 |
160 | override def exitWhile(ctx: WhileContext) =
161 | ctx.value = While(ctx.bool.value, ctx.statement.value)
162 |
163 | // Other rules handle remaining language constructs
164 | ````
165 |
166 | The parser:
167 |
168 | - Uses the same grammar as the interpreter
169 | - Constructs identical AST structure
170 | - Enables code reuse between interpreter and transpiler
171 |
172 | ---
173 |
174 | ## Main Entry Point
175 |
176 | The transpiler entry point generates and prints Scala code:
177 |
178 | ```scala linenums="1"
179 | --8<--
180 | src/main/scala/whilelang/compiler/Main.scala
181 | --8<--
182 | ```
183 |
184 | ````scala
185 | package whilelang.compiler
186 |
187 | def action = Runner(program => println(program.meaning))
188 |
189 | @main def main(file: String) = action(file)
190 | ````
191 |
192 | Execution flow:
193 |
194 | 1. Parses Whilelang source file
195 | 2. Builds AST using shared parser rules
196 | 3. Converts AST to Scala code via `meaning` extension
197 | 4. Outputs generated Scala to console
198 |
199 | ---
200 |
201 | ## Shared Utilities
202 |
203 | ### Runner Class
204 |
205 | Handles file processing and error management:
206 |
207 | ````scala
208 | object Runner:
209 | def apply(action: Program => Unit)(file: String) =
210 | sourceCode(file).flatMap(walk) match
211 | case Success(program) => action(program)
212 | case Failure(e) => handleError(e)
213 | ````
214 |
215 | ### Walker Component
216 |
217 | Reused from interpreter implementation:
218 |
219 | ````scala
220 | object Walker:
221 | def walk(source: String)(using listener: MyListener) = Try:
222 | val lexer = WhilelangLexer(CharStreams.fromString(source))
223 | val parser = WhilelangParser(CommonTokenStream(lexer))
224 | ParseTreeWalker().walk(listener, parser.program)
225 | listener.program
226 | ````
227 |
228 | ### ContextValue Trait
229 |
230 | Provides tree property management for AST construction:
231 |
232 | ````scala
233 | trait ContextValue:
234 | given ParseTreeProperty[Any] = ParseTreeProperty[Any]()
235 |
236 | extension (tree: ParseTree)
237 | def value[E]: E = // Retrieves typed AST node
238 | def value_=(v: Any) = // Stores parsed value
239 | ````
240 |
241 | ---
242 |
243 | This transpiler demonstrates:
244 |
245 | - Source-to-source compilation techniques
246 | - AST-based code generation
247 | - Shared infrastructure with interpreter
248 | - Idiomatic Scala code generation
249 | - Automatic variable management
250 | - Structural preservation of control flow
251 |
252 | The generated Scala code maintains the original program's behavior while benefiting from Scala's type system and runtime environment.
253 |
--------------------------------------------------------------------------------
/examples/fibonacci.while:
--------------------------------------------------------------------------------
1 | print "Fibonacci Sequence";
2 | a := 0;
3 | b := 1;
4 | while b <= 1000000 do {
5 | print b;
6 | b := a + b;
7 | a := b - a
8 | }
9 |
--------------------------------------------------------------------------------
/examples/hello.while:
--------------------------------------------------------------------------------
1 | print "Hello World"
2 |
--------------------------------------------------------------------------------
/examples/sum.while:
--------------------------------------------------------------------------------
1 | print "Enter the first number:";
2 | a := read;
3 | print "Enter the second number:";
4 | b := read;
5 | sum := a + b;
6 | print "The sum is:";
7 | print sum
8 |
--------------------------------------------------------------------------------
/mkdocs.yml:
--------------------------------------------------------------------------------
1 | site_name: WhileLang
2 | site_description: A minimalistic programming language built with Scala and ANTLR
3 | site_url: https://your-domain.com/whilelang
4 | repo_name: lrlucena/whilelang
5 | repo_url: https://github.com/lrlucena/whilelang
6 | edit_uri: edit/main/docs/
7 |
8 | theme:
9 | name: material
10 | features:
11 | - navigation.tabs
12 | - navigation.sections
13 | - navigation.top
14 | - toc.integrate
15 | - content.code.annotate
16 | - content.code.copy
17 | palette:
18 | - scheme: default
19 | primary: indigo
20 | accent: blue
21 | toggle:
22 | icon: material/weather-night
23 | name: Switch to dark mode
24 | - scheme: slate
25 | primary: blue
26 | accent: blue
27 | toggle:
28 | icon: material/weather-sunny
29 | name: Switch to light mode
30 | icon:
31 | repo: fontawesome/brands/github
32 |
33 | markdown_extensions:
34 | - admonition
35 | - pymdownx.details
36 | - pymdownx.superfences
37 | - pymdownx.highlight:
38 | anchor_linenums: true
39 | - pymdownx.inlinehilite
40 | - pymdownx.tabbed:
41 | alternate_style: true
42 | - pymdownx.tasklist:
43 | custom_checkbox: true
44 | - pymdownx.snippets
45 | - attr_list
46 | - tables
47 | - toc:
48 | permalink: true
49 |
50 | plugins:
51 | - search
52 | # - minify:
53 | # minify_html: true
54 |
55 | nav:
56 | - Home: index.md
57 | - Interpreter: interpreter.md
58 | - Transpiler: transpiler.md
59 | - Examples:
60 | - Hello World: examples/hello.md
61 | - Fibonacci: examples/fib.md
62 | - API Reference: api.md
63 | - Changelog: changelog.md
64 |
65 | extra:
66 | social:
67 | - icon: fontawesome/brands/github
68 | link: https://github.com/lrlucena/whilelang
69 |
--------------------------------------------------------------------------------
/project/antlr4.sbt:
--------------------------------------------------------------------------------
1 | addSbtPlugin("com.simplytyped" % "sbt-antlr4" % "0.8.3")
2 |
--------------------------------------------------------------------------------
/project/assembly.sbt:
--------------------------------------------------------------------------------
1 | addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "2.3.1")
2 |
--------------------------------------------------------------------------------
/project/build.properties:
--------------------------------------------------------------------------------
1 | sbt.version=1.10.11
2 |
--------------------------------------------------------------------------------
/project/metals.sbt:
--------------------------------------------------------------------------------
1 | // DO NOT EDIT! This file is auto-generated.
2 |
3 | // This file enables sbt-bloop to create bloop config files.
4 |
5 | addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % "1.5.15")
6 |
7 |
--------------------------------------------------------------------------------
/src/main/antlr4/whilelang/parser/Whilelang.g4:
--------------------------------------------------------------------------------
1 | grammar Whilelang;
2 |
3 | program : seqStatement;
4 |
5 | seqStatement: statement (';' statement)* ;
6 |
7 | statement: ID ':=' expression # attrib
8 | | 'skip' # skip
9 | | 'if' bool 'then' statement 'else' statement # if
10 | | 'while' bool 'do' statement # while
11 | | 'print' Text # print
12 | | 'print' expression # write
13 | | '{' seqStatement '}' # block
14 | ;
15 |
16 | expression: INT # int
17 | | 'read' # read
18 | | ID # id
19 | | expression '*' expression # binOp
20 | | expression ('+'|'-') expression # binOp
21 | | '(' expression ')' # expParen
22 | ;
23 |
24 | bool: ('true'|'false') # boolean
25 | | expression '=' expression # relOp
26 | | expression '<=' expression # relOp
27 | | 'not' bool # not
28 | | bool 'and' bool # and
29 | | '(' bool ')' # boolParen
30 | ;
31 |
32 | INT: ('0'..'9')+ ;
33 | ID: ('a'..'z')+;
34 | Text: '"' .*? '"';
35 | Space: [ \t\n\r] -> skip;
36 |
--------------------------------------------------------------------------------
/src/main/scala/whilelang/compiler/Main.scala:
--------------------------------------------------------------------------------
1 | package whilelang.compiler
2 |
3 | import whilelang.util.Runner
4 |
5 | val action = Runner(program => println(program.meaning))
6 |
7 | @main def main(file: String) = action(file)
8 |
--------------------------------------------------------------------------------
/src/main/scala/whilelang/compiler/Semantics.scala:
--------------------------------------------------------------------------------
1 | package whilelang.compiler
2 |
3 | import whilelang.parser.*
4 | import whilelang.parser.Bool.*
5 | import whilelang.parser.Expression.*
6 | import whilelang.parser.Statement.*
7 |
8 | import scala.collection.mutable.Set as MSet
9 |
10 | type Ids = MSet[String]
11 | given Ids = MSet[String]()
12 |
13 | def vars(ids: Ids) =
14 | if ids.nonEmpty then s"var ${ids.mkString(", ")} = 0" else ""
15 |
16 | extension (src: Element)(using ids: Ids)
17 | def meaning: String = m
18 |
19 | private def op(s: String)(lhs: Element, rhs: Element) = s"(${lhs.m} $s ${rhs.m})"
20 |
21 | private def m: String = src match
22 | case If(cond, tSmt, eSmt) => s"if ${cond.m} then\n ${tSmt.m}\nelse\n ${eSmt.m}"
23 | case Print(exp: Expression) => s"println(${exp.m})"
24 | case Print(text: String) => s"println(\"$text\")"
25 | case While(cond, doSmt) => s"while ${cond.m} do\n ${doSmt.m}"
26 | case SeqStatement(stmts) => stmts.map(_.m).mkString("\n").replaceAll("\n", "\n ")
27 | case Attrib(id, exp) => ids.add(id); s"$id = ${exp.m}"
28 | case Program(seq) => s"@main def main() =\n ${vars(ids)}\n ${seq.m}"
29 | case Skip => "()"
30 | case Read => "readInt()"
31 | case Id(id) => id
32 | case Integer(value) => s"$value"
33 | case Boole(b) => s"$b"
34 | case Not(b) => s"(!${b.m})"
35 | case ExpSum(lhs, rhs) => op("+")(lhs, rhs)
36 | case ExpSub(lhs, rhs) => op("-")(lhs, rhs)
37 | case ExpMult(lhs, rhs) => op("*")(lhs, rhs)
38 | case ExpEq(lhs, rhs) => op("==")(lhs, rhs)
39 | case ExpLe(lhs, rhs) => op("<=")(lhs, rhs)
40 | case And(lhs, rhs) => op("&&")(lhs, rhs)
41 | case _ => "~~~ Not Implemented ~~~"
42 |
--------------------------------------------------------------------------------
/src/main/scala/whilelang/interpreter/Main.scala:
--------------------------------------------------------------------------------
1 | package whilelang.interpreter
2 |
3 | import whilelang.util.Runner
4 |
5 | val action = Runner(program => program.execute())
6 |
7 | @main def main(file: String) = action(file)
8 |
--------------------------------------------------------------------------------
/src/main/scala/whilelang/interpreter/Semantics.scala:
--------------------------------------------------------------------------------
1 | package whilelang.interpreter
2 |
3 | import scala.collection.mutable.Map as MMap
4 | import whilelang.parser.*
5 | import whilelang.parser.Statement.*
6 | import whilelang.parser.Expression.*
7 | import whilelang.parser.Bool.*
8 |
9 | type Environment = MMap[String, Int]
10 | given Environment = MMap[String, Int]()
11 |
12 | extension (stmt: Statement)(using env: Environment)
13 | def execute(): Unit = stmt match
14 | case If(cond, tSmt, eSmt) => (if cond.value then tSmt else eSmt).execute()
15 | case Print(exp: Expression) => println(exp.value)
16 | case Print(text: String) => println(text)
17 | case While(cond, doSmt) => while cond.value do doSmt.execute()
18 | case SeqStatement(stmts) => stmts.foreach(_.execute())
19 | case Attrib(id, exp) => env += id -> exp.value
20 | case Program(seq) => seq.execute()
21 | case Skip | _ =>
22 |
23 | extension (exp: Expression)(using env: Environment)
24 | def value: Int = exp match
25 | case Read => io.StdIn.readInt()
26 | case Id(id) => env.getOrElseUpdate(id, 0)
27 | case Integer(value) => value
28 | case ExpSum(lhs, rhs) => lhs.value + rhs.value
29 | case ExpSub(lhs, rhs) => lhs.value - rhs.value
30 | case ExpMult(lhs, rhs) => lhs.value * rhs.value
31 | case null | _ => 0
32 |
33 | extension (exp: Bool)
34 | def value: Boolean = exp match
35 | case Boole(b) => b
36 | case ExpEq(lhs, rhs) => lhs.value == rhs.value
37 | case ExpLe(lhs, rhs) => lhs.value <= rhs.value
38 | case Not(b) => !b.value
39 | case And(lhs, rhs) => lhs.value && rhs.value
40 | case null | _ => true
41 |
--------------------------------------------------------------------------------
/src/main/scala/whilelang/parser/MyListener.scala:
--------------------------------------------------------------------------------
1 | package whilelang.parser
2 |
3 | import scala.compiletime.uninitialized
4 | import whilelang.parser.Bool.*
5 | import whilelang.parser.Expression.*
6 | import whilelang.parser.Statement.*
7 | import whilelang.parser.WhilelangBaseListener as BaseListener
8 | import whilelang.parser.WhilelangParser.*
9 | import whilelang.util.ContextValue
10 |
11 | class MyListener extends BaseListener with ContextValue:
12 |
13 | var program: Program = uninitialized
14 |
15 | override def exitProgram(ctx: ProgramContext) =
16 | program = Program(ctx.seqStatement.value)
17 |
18 | override def exitSeqStatement(ctx: SeqStatementContext) = ctx.value :
19 | SeqStatement(ctx.statement.map(_.value[Statement]))
20 |
21 | override def exitAttrib(ctx: AttribContext) = ctx.value :
22 | Attrib(ctx.ID.text, ctx.expression.value)
23 |
24 | override def exitSkip(ctx: SkipContext) = ctx.value :
25 | Skip
26 |
27 | override def exitIf(ctx: IfContext) = ctx.value :
28 | val Seq(thenStat, elseStat) = ctx.statement.map(_.value[Statement])
29 | If(ctx.bool.value, thenStat, elseStat)
30 |
31 | override def exitWhile(ctx: WhileContext) = ctx.value :
32 | While(ctx.bool.value, ctx.statement.value)
33 |
34 | override def exitPrint(ctx: PrintContext) = ctx.value :
35 | Print(ctx.Text.text.drop(1).dropRight(1))
36 |
37 | override def exitWrite(ctx: WriteContext) = ctx.value :
38 | Print(ctx.expression.value)
39 |
40 | override def exitBlock(ctx: BlockContext) = ctx.value :
41 | ctx.seqStatement.value
42 |
43 | override def exitRead(ctx: ReadContext) = ctx.value :
44 | Read
45 |
46 | override def exitId(ctx: IdContext) = ctx.value :
47 | Id(ctx.ID.text)
48 |
49 | override def exitExpParen(ctx: ExpParenContext) = ctx.value :
50 | ctx.expression.value
51 |
52 | override def exitInt(ctx: IntContext) = ctx.value :
53 | Integer(ctx.text.toInt)
54 |
55 | override def exitBinOp(ctx: BinOpContext) = ctx.value :
56 | val Seq(lhs, rhs) = ctx.expression.map(_.value[Expression])
57 | ctx(1).text match
58 | case "*" => ExpMult(lhs, rhs)
59 | case "-" => ExpSub(lhs, rhs)
60 | case _ => ExpSum(lhs, rhs)
61 |
62 | override def exitNot(ctx: NotContext) = ctx.value :
63 | Not(ctx.bool.value)
64 |
65 | override def exitBoolean(ctx: BooleanContext) = ctx.value :
66 | Boole(ctx.text == "true")
67 |
68 | override def exitAnd(ctx: AndContext) = ctx.value :
69 | And(ctx.bool(0).value, ctx.bool(1).value)
70 |
71 | override def exitBoolParen(ctx: BoolParenContext) = ctx.value :
72 | ctx.bool.value
73 |
74 | override def exitRelOp(ctx: RelOpContext) = ctx.value :
75 | val Seq(lhs, rhs) = ctx.expression.map(_.value[Expression])
76 | ctx(1).text match
77 | case "=" => ExpEq(lhs, rhs)
78 | case _ => ExpLe(lhs, rhs)
--------------------------------------------------------------------------------
/src/main/scala/whilelang/parser/Syntax.scala:
--------------------------------------------------------------------------------
1 | package whilelang.parser
2 |
3 | trait Element
4 |
5 | enum Statement extends Element:
6 | case Skip
7 | case If(condition: Bool, thenSmt: Statement, elseSmt: Statement)
8 | case While(condition: Bool, doSmt: Statement)
9 | case Print(exp: String | Expression)
10 | case SeqStatement(statements: Seq[Statement])
11 | case Attrib(id: String, exp: Expression)
12 | case Program(statements: SeqStatement)
13 |
14 | enum Expression extends Element:
15 | case Read
16 | case Id(id: String)
17 | case Integer(exp: Int)
18 | case ExpSum(lhs: Expression, rhs: Expression)
19 | case ExpSub(lhs: Expression, rhs: Expression)
20 | case ExpMult(lhs: Expression, rhs: Expression)
21 |
22 | enum Bool extends Element:
23 | case Boole(b: Boolean)
24 | case ExpEq(lhs: Expression, rhs: Expression)
25 | case ExpLe(lhs: Expression, rhs: Expression)
26 | case Not(b: Bool)
27 | case And(lhs: Bool, rhs: Bool)
28 |
--------------------------------------------------------------------------------
/src/main/scala/whilelang/util/ContextValue.scala:
--------------------------------------------------------------------------------
1 | package whilelang.util
2 |
3 | import org.antlr.v4.runtime.tree.{ParseTree, ParseTreeProperty}
4 | import scala.jdk.CollectionConverters.ListHasAsScala
5 | import whilelang.parser.Element
6 |
7 | trait ContextValue:
8 | private val values = ParseTreeProperty[Any]()
9 |
10 | extension (tree: ParseTree)
11 | def apply(i: Int): ParseTree = tree.getChild(i)
12 | def text: String = tree.getText
13 | def value[E <: Any]: E = values.get(tree).asInstanceOf[E]
14 | def value(v: Any): Unit = values.put(tree, v)
15 | //def apply(v: Any): Unit = values.put(tree, v)
16 | //def `=`(v: Any): Unit = values.put(tree, v)
17 | //def value_=(v: Any): Unit = values.put(tree, v)
18 |
19 | extension[E] (list: java.util.List[E])
20 | def map[T](f: E => T): Seq[T] = list.asScala.toSeq.map(f)
21 |
--------------------------------------------------------------------------------
/src/main/scala/whilelang/util/Runner.scala:
--------------------------------------------------------------------------------
1 | package whilelang.util
2 |
3 | import whilelang.parser.MyListener as Listener
4 | import whilelang.parser.Statement.Program
5 |
6 | import java.io.FileNotFoundException
7 | import scala.util.{Failure, Success, Try}
8 |
9 | object Runner:
10 | def apply(action: Program => Unit)(file: String): Unit =
11 | for source <- sourceCode(file) yield
12 | Walker(Listener())(source) match
13 | case Success(program) => action(program)
14 | case Failure(_: FileNotFoundException) => println("File not found")
15 | case Failure(e) => println("Error: " + e.printStackTrace())
16 |
17 | def sourceCode(file: String): Try[String] = Try:
18 | io.Source.fromFile(file).getLines.mkString("\n")
19 |
--------------------------------------------------------------------------------
/src/main/scala/whilelang/util/Walker.scala:
--------------------------------------------------------------------------------
1 | package whilelang.util
2 |
3 | import org.antlr.v4.runtime.*
4 | import org.antlr.v4.runtime.misc.ParseCancellationException
5 | import org.antlr.v4.runtime.tree.ParseTreeWalker
6 | import whilelang.parser.Statement.Program
7 | import whilelang.parser.{Statement, MyListener as Listener, WhilelangLexer as Lexer, WhilelangParser as Parser}
8 |
9 | import scala.util.Try
10 |
11 | object Walker:
12 | def apply(listener: Listener)(source: String): Try[Program] = Try:
13 | val lexer = Lexer(CharStreams.fromString(source))
14 | val parser = Parser(CommonTokenStream(lexer))
15 | addListener(lexer, parser)
16 | ParseTreeWalker().walk(listener, parser.program)
17 | listener.program
18 |
19 | object ThrowingErrorListener extends BaseErrorListener:
20 | override def syntaxError(r: Recognizer[?, ?], off: Any, line: Int, col: Int, msg: String, e: RecognitionException) =
21 | throw ParseCancellationException(s"line $line:$col $msg")
22 |
23 | private def addListener(r: Recognizer[?, ?]*): Unit = r.foreach: r =>
24 | r.removeErrorListeners()
25 | r.addErrorListener(ThrowingErrorListener)
26 |
--------------------------------------------------------------------------------
/src/plugins.sbt:
--------------------------------------------------------------------------------
1 | addSbtPlugin("ch.epfl.scala" % "sbt-scala3-migrate" % "0.7.2")
2 |
--------------------------------------------------------------------------------
/while:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | java -Xmx1879m -jar ./target/scala-3.6.4/WhileLang-assembly-1.6.4.jar $1
--------------------------------------------------------------------------------