├── .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 | [![Codacy Badge](https://api.codacy.com/project/badge/Grade/b1705795c5f74b9289b6f4c942dd5911)](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 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 26 | 27 | 28 | 29 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 |
InterpreterTranspiler (Compiler)
Grammar 24 | Grammar (29 lines) 25 |
Abstract Syntax 30 | Abstract Syntax (23 lines) 31 |
SemanticsSemantics (35 lines)Semantics (34 lines)
Parser Rules 41 | Listener (56 lines) 42 |
MainMain (4 lines)Main (4 lines)
Utility Classes 52 | Walker (20 lines)
53 | Runner (14 lines)
54 | ContextValue (13 lines) 55 |
Total194 lines193 lines
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 | [![Codacy Badge](https://api.codacy.com/project/badge/Grade/b1705795c5f74b9289b6f4c942dd5911)](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 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 31 | 32 | 33 | 34 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 |
InterpreterTranspiler (Compiler)
Grammar 29 | Grammar 30 |
Abstract Syntax 35 | Abstract Syntax 36 |
SemanticsSemantics (35 lines)Semantics (34 lines)
Parser Rules 46 | Listener (56 lines) 47 |
MainMain (4 lines)Main (4 lines)
Utility Classes 57 | Walker (20 lines)
58 | Runner (14 lines)
59 | ContextValue (13 lines) 60 |
Total194 lines193 lines
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 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 40 | 46 | 47 | 48 | 49 | 50 | 61 | 74 | 75 | 76 | 77 | 78 | 90 | 103 | 104 | 105 |
ProgramWhilelangGenerated Scala
Hello World 36 | ```ruby 37 | print "Hello World" 38 | ``` 39 | 41 | ```scala 42 | @main def main() = 43 | println("Hello World") 44 | ``` 45 |
Sum of Numbers 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 | 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 |
Fibonacci Sequence 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 | 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 |
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 --------------------------------------------------------------------------------