├── .gitignore ├── LICENSE ├── README.md ├── _config.yml ├── compiler-examples ├── ck-compiler │ ├── README.md │ ├── pom.xml │ └── src │ │ ├── main │ │ └── java │ │ │ └── com │ │ │ └── arjunsk │ │ │ └── compiler │ │ │ └── ck │ │ │ ├── domain │ │ │ ├── token │ │ │ │ ├── Token.java │ │ │ │ └── support │ │ │ │ │ └── TokenType.java │ │ │ └── tree │ │ │ │ ├── ParseTree.java │ │ │ │ ├── Visitable.java │ │ │ │ └── nodes │ │ │ │ ├── common │ │ │ │ └── TerminalNode.java │ │ │ │ └── grammer │ │ │ │ ├── ParserRuleContext.java │ │ │ │ └── impl │ │ │ │ ├── LetContext.java │ │ │ │ ├── ProgramContext.java │ │ │ │ ├── ShowContext.java │ │ │ │ └── StatementContext.java │ │ │ ├── exceptions │ │ │ ├── LexerException.java │ │ │ ├── ParserException.java │ │ │ └── SemanticException.java │ │ │ ├── lexer │ │ │ └── Lexer.java │ │ │ ├── parser │ │ │ └── Parser.java │ │ │ └── visitor │ │ │ ├── SimplerLangBaseVisitor.java │ │ │ ├── Visitor.java │ │ │ ├── codegenerator │ │ │ └── CodeGeneratorVisitor.java │ │ │ ├── interpreter │ │ │ └── InterpreterVisitor.java │ │ │ └── semantic │ │ │ └── SemanticAnalyzer.java │ │ └── test │ │ ├── java │ │ └── com │ │ │ └── arjunsk │ │ │ └── compiler │ │ │ ├── ck │ │ │ ├── CkCompilerTest.java │ │ │ ├── lexer │ │ │ │ └── LexerTest.java │ │ │ ├── parser │ │ │ │ └── ParserTest.java │ │ │ └── visitor │ │ │ │ └── semantic │ │ │ │ └── SemanticAnalyzerTest.java │ │ │ └── utils │ │ │ └── FileReaderUtil.java │ │ └── resources │ │ └── input │ │ └── CgSample.ck ├── lisp-compiler │ ├── pom.xml │ └── src │ │ ├── main │ │ └── java │ │ │ └── com │ │ │ └── arjunsk │ │ │ └── compiler │ │ │ └── lisp │ │ │ ├── core │ │ │ ├── emitter │ │ │ │ └── .gitkeep │ │ │ ├── lexer │ │ │ │ └── Lexer.java │ │ │ ├── parser │ │ │ │ └── Parser.java │ │ │ ├── transformer │ │ │ │ └── Transformer.java │ │ │ └── visitor │ │ │ │ └── .gitkeep │ │ │ ├── domain │ │ │ ├── ast │ │ │ │ ├── AstNode.java │ │ │ │ └── LispAstNode.java │ │ │ └── lexer │ │ │ │ ├── Token.java │ │ │ │ └── support │ │ │ │ └── TokenType.java │ │ │ └── exceptions │ │ │ ├── LexerException.java │ │ │ └── ParserException.java │ │ └── test │ │ ├── java │ │ └── com │ │ │ └── arjunsk │ │ │ └── compiler │ │ │ ├── lisp │ │ │ └── core │ │ │ │ ├── lexer │ │ │ │ └── LexerTest.java │ │ │ │ └── parser │ │ │ │ └── ParserTest.java │ │ │ └── utils │ │ │ └── FileReaderUtil.java │ │ └── resources │ │ └── input │ │ └── code.lsp ├── pom.xml └── svg-compiler │ ├── pom.xml │ └── src │ ├── main │ └── java │ │ └── com │ │ └── arjunsk │ │ └── compiler │ │ └── svg │ │ ├── core │ │ ├── generation │ │ │ └── CodeGenerator.java │ │ ├── parsing │ │ │ ├── Lexer.java │ │ │ └── Parser.java │ │ └── transforming │ │ │ └── Transformer.java │ │ ├── domain │ │ ├── lexer │ │ │ ├── ast │ │ │ │ ├── AstNode.java │ │ │ │ └── support │ │ │ │ │ └── AstNodeClass.java │ │ │ └── token │ │ │ │ ├── Token.java │ │ │ │ └── support │ │ │ │ └── TokenType.java │ │ └── transformer │ │ │ └── ast │ │ │ └── SvgAstNode.java │ │ └── exceptions │ │ ├── LexerException.java │ │ ├── ParserException.java │ │ └── TransformerException.java │ └── test │ ├── java │ └── com │ │ └── arjunsk │ │ └── compiler │ │ ├── svg │ │ └── core │ │ │ ├── generation │ │ │ └── CodeGeneratorTest.java │ │ │ ├── parsing │ │ │ ├── LexerTest.java │ │ │ └── ParserTest.java │ │ │ └── transforming │ │ │ └── TransformerTest.java │ │ └── utils │ │ └── FileReaderUtil.java │ └── resources │ ├── lexer │ └── code.ck │ └── output │ └── code.svg ├── parser-examples ├── antlr-examples │ ├── antlr-listener-example │ │ ├── pom.xml │ │ └── src │ │ │ └── main │ │ │ ├── antlr4 │ │ │ └── com │ │ │ │ └── arjunsk │ │ │ │ └── parser │ │ │ │ └── antlr │ │ │ │ └── SimplerLang.g4 │ │ │ └── java │ │ │ └── com │ │ │ └── arjunsk │ │ │ └── parser │ │ │ └── antlr │ │ │ └── listener │ │ │ ├── AntlrListenerDriver.java │ │ │ └── support │ │ │ └── SimplerLangListenerImpl.java │ ├── antlr-visitor-example1 │ │ ├── pom.xml │ │ └── src │ │ │ └── main │ │ │ ├── antlr4 │ │ │ └── com │ │ │ │ └── arjunsk │ │ │ │ └── parser │ │ │ │ └── antlr │ │ │ │ └── Calculator.g4 │ │ │ └── java │ │ │ └── com │ │ │ └── arjunsk │ │ │ └── parser │ │ │ └── antlr │ │ │ └── visitor │ │ │ ├── AntlrVisitorDriver1.java │ │ │ └── support │ │ │ └── CalculatorVisitorImpl.java │ ├── antlr-visitor.example2 │ │ ├── pom.xml │ │ └── src │ │ │ └── main │ │ │ ├── antlr4 │ │ │ └── com │ │ │ │ └── arjunsk │ │ │ │ └── parser │ │ │ │ └── antlr │ │ │ │ └── SimplerLang.g4 │ │ │ └── java │ │ │ └── com │ │ │ └── arjunsk │ │ │ └── parser │ │ │ └── antlr │ │ │ └── listener │ │ │ ├── AntlrVisitorDriver2.java │ │ │ └── support │ │ │ └── SimplerLangVisitorImpl.java │ └── pom.xml ├── javaparser-example │ ├── pom.xml │ └── src │ │ └── main │ │ ├── java │ │ └── com │ │ │ └── arjunsk │ │ │ └── parser │ │ │ └── javaparser │ │ │ └── JavaparserDriver.java │ │ └── resources │ │ └── input │ │ └── SimpleProgram.java ├── jdt-example │ ├── pom.xml │ └── src │ │ └── main │ │ ├── java │ │ └── com │ │ │ └── arjunsk │ │ │ └── parser │ │ │ └── jdt │ │ │ ├── JdtDriver.java │ │ │ └── ast │ │ │ └── JdtAstVisitor.java │ │ └── resources │ │ └── input │ │ └── SimpleProgram.java └── pom.xml └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | ############################## 2 | ## Java 3 | ############################## 4 | .mtj.tmp/ 5 | *.class 6 | *.jar 7 | *.war 8 | *.ear 9 | *.nar 10 | hs_err_pid* 11 | 12 | ############################## 13 | ## Maven 14 | ############################## 15 | target/ 16 | pom.xml.tag 17 | pom.xml.releaseBackup 18 | pom.xml.versionsBackup 19 | pom.xml.next 20 | pom.xml.bak 21 | release.properties 22 | dependency-reduced-pom.xml 23 | buildNumber.properties 24 | .mvn/timing.properties 25 | .mvn/wrapper/maven-wrapper.jar 26 | 27 | ############################## 28 | ## Gradle 29 | ############################## 30 | bin/ 31 | build/ 32 | .gradle 33 | .gradletasknamecache 34 | gradle-app.setting 35 | !gradle-wrapper.jar 36 | 37 | ############################## 38 | ## IntelliJ 39 | ############################## 40 | out/ 41 | .idea/ 42 | .idea_modules/ 43 | *.iml 44 | *.ipr 45 | *.iws 46 | 47 | ############################## 48 | ## Eclipse 49 | ############################## 50 | .settings/ 51 | bin/ 52 | tmp/ 53 | .metadata 54 | .classpath 55 | .project 56 | *.tmp 57 | *.bak 58 | *.swp 59 | *~.nib 60 | local.properties 61 | .loadpath 62 | .factorypath 63 | 64 | ############################## 65 | ## NetBeans 66 | ############################## 67 | nbproject/private/ 68 | build/ 69 | nbbuild/ 70 | dist/ 71 | nbdist/ 72 | nbactions.xml 73 | nb-configuration.xml 74 | 75 | ############################## 76 | ## OS X 77 | ############################## 78 | .DS_Store 79 | 80 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Arjun SK 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 | # Codekrypt Compiler 2 | This repo contains sub modules which helped me understand compilers better. 3 | 4 | ## Project Modules 5 | 6 | ### Compiler Examples 7 | 8 | #### CK(Codekrypt) Compiler 9 | This compiler was developed ground up as a part of learning. This is adapted from `ANLR Examples` module. 10 | 1. [CK Compiler src](/compiler-examples/ck-compiler) 11 | 2. [ASM Code Generation](https://github.com/arjunsk/java-bytecode/tree/master/java-asm/ow2-asm-example/src/main/java/com/arjunsk/asm/asmifier) 12 | 13 | #### SVG Compiler (needs review) 14 | 1. [Web App](https://kosamari.github.io/sbn/) 15 | 2. [Medium Article](https://medium.com/@kosamari/how-to-be-a-compiler-make-a-compiler-with-javascript-4a8a13d473b4) 16 | 17 | #### LISP Compiler (on hold) 18 | Trying to port [Tiny-Compiler](https://github.com/jamiebuilds/the-super-tiny-compiler) to Java. 19 | 1. [Github](https://github.com/jamiebuilds/the-super-tiny-compiler/blob/master/the-super-tiny-compiler.js) 20 | 21 | ### Parser Examples 22 | 23 | #### ANTLR Examples 24 | 1. [Visitor](https://www.javahelps.com/2019/04/antlr-hello-world-arithmetic-expression.html) 25 | 2. [Listener](https://shalithasuranga.medium.com/build-your-own-programming-language-with-antlr-5201955537a5) 26 | 27 | #### Javaparser Example 28 | 1. [StackOverflow](https://stackoverflow.com/questions/58611706/javaparser-parsing-and-generating-java-code) 29 | 30 | #### JDT Example 31 | Functions similar to what is provided by Eclipse IDE. (project, workspace, code) 32 | 1. [ProgramCreek](https://www.programcreek.com/2011/01/best-java-development-tooling-jdt-and-astparser-tutorials/) 33 | 34 | 35 | ## Trouble Shooting 36 | 1. If the project is failing in the `ANTLR Examples` submodules, please build the project first using. 37 | ```shell script 38 | mvn clean install 39 | ``` 40 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-hacker -------------------------------------------------------------------------------- /compiler-examples/ck-compiler/README.md: -------------------------------------------------------------------------------- 1 | ## CK Compiler 2 | This is a project I created for learning internals of compilers. The `SimplerLang` grammar is taken from [this](https://shalithasuranga.medium.com/build-your-own-programming-language-with-antlr-5201955537a5) post. 3 | 4 | ### To compile `.ck` to `.class` 5 | 1. Open [CompilerTest.java](src/test/java/com/arjunsk/compiler/ck/CkCompilerTest.java) 6 | 2. Run `compile()` 7 | 3. Look for the `output/CgSample.class` generated under root. 8 | 4. To see the source code of `CgSample.class`, use IntelliJ decompiler. 9 | 10 | ### Grammar 11 | ```antlrv4 12 | grammar simplerlang; 13 | 14 | program : statement+; 15 | statement : let | show ; 16 | 17 | let : VAR '=' INT ; 18 | show : 'show' (INT | VAR) ; 19 | 20 | VAR : [a-z]+ ; 21 | INT : [0–9]+ ; 22 | WS : [ \n\t]+ -> skip; 23 | ``` 24 | 25 | ### Features 26 | 1. Used a simple grammar to focus more on `compiler phases` rather than `language support`. 27 | 2. Took `ANTLR` generated code as a reference. 28 | 2. Implemented [Parse Tree](src/main/java/com/arjunsk/compiler/ck/domain/tree/ParseTree.java) & created [Visitable](src/main/java/com/arjunsk/compiler/ck/domain/tree/Visitable.java) Grammar Nodes. 29 | 3. Implemented [Visitor](src/main/java/com/arjunsk/compiler/ck/visitor/Visitor.java) for writing business logic on Tree nodes. 30 | 4. Implemented [CodeGeneration](src/main/java/com/arjunsk/compiler/ck/visitor/codegenerator/CodeGeneratorVisitor.java) using `Java ASM`. 31 | 5. Implemented [Interpreter](src/main/java/com/arjunsk/compiler/ck/visitor/interpreter/InterpreterVisitor.java) & [Semantic Analyzer](src/main/java/com/arjunsk/compiler/ck/visitor/semantic/SemanticAnalyzer.java). 32 | 33 | ### TODO 34 | * ~~Implement Semantic Analyser.~~ 35 | * ~~Implement custom filename.(Unsupported)~~ 36 | * ~~Implement Parse Tree.~~ 37 | * ~~Implement Visitor Pattern.~~ 38 | * ~~Implement byte code generation~~ 39 | 40 | ### Compiler Phases 41 | 1. Lexical Analysis [Done] 42 | 2. Syntactic Analysis (ie Parsing) [Done] 43 | 3. Semantic analysis [Done] & Intermediate Code Generation [NA] 44 | 4. Optimization (optional) 45 | 5. Code Generation [Done] 46 | 47 | Intermediate Code Generation: code gets converted to independent intermediate code. We are not doing this in `ck-compiler`. 48 | We could use `LLVM` as Backend for implementing this feature. 49 | 50 | ### Reference: 51 | 1. [Java ASM](https://github.com/arjunsk/java-bytecode/tree/master/java-asm/ow2-asm-example/src/main/java/com/arjunsk/asm/asmifier) 52 | 2. [Ops Code](https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html) 53 | 3. [Lecture Note](https://www.radford.edu/~nokie/classes/380/phases.html) 54 | 4. [LLVM Backend](https://llvm.org/docs/WritingAnLLVMBackend.html) 55 | 56 | > Do check out other modules in this project for better understanding.️ -------------------------------------------------------------------------------- /compiler-examples/ck-compiler/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | com.arjunsk 9 | compiler-examples 10 | 1.0-SNAPSHOT 11 | 12 | 13 | ck-compiler 14 | Codekrypt Compiler 15 | 16 | 17 | 18 | arjunsk 19 | Arjun SK 20 | https://github.com/arjunsk 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | org.ow2.asm 29 | asm 30 | ${ow2-asm.version} 31 | 32 | 33 | 34 | 35 | org.ow2.asm 36 | asm-util 37 | ${ow2-asm.version} 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /compiler-examples/ck-compiler/src/main/java/com/arjunsk/compiler/ck/domain/token/Token.java: -------------------------------------------------------------------------------- 1 | package com.arjunsk.compiler.ck.domain.token; 2 | 3 | import com.arjunsk.compiler.ck.domain.token.support.TokenType; 4 | 5 | /** Output of Lexer. Contains TokenType and Token Value. */ 6 | public class Token { 7 | 8 | private final TokenType type; 9 | 10 | private final String value; 11 | 12 | public Token(TokenType type) { 13 | this.type = type; 14 | this.value = null; 15 | } 16 | 17 | public Token(TokenType type, String value) { 18 | this.type = type; 19 | this.value = value; 20 | } 21 | 22 | public TokenType getType() { 23 | return type; 24 | } 25 | 26 | public String getValue() { 27 | return value; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /compiler-examples/ck-compiler/src/main/java/com/arjunsk/compiler/ck/domain/token/support/TokenType.java: -------------------------------------------------------------------------------- 1 | package com.arjunsk.compiler.ck.domain.token.support; 2 | 3 | /** 4 | * Our code will contain only SHOW, VARIABLE, EQUALS_OPERATOR or NUMBER 5 | * 6 | *
 7 |  *   a = 10
 8 |  *   show a
 9 |  *   show 20
10 |  *
11 |  * 
12 | */ 13 | public enum TokenType { 14 | SHOW, // Key word 15 | 16 | EQUALS_OPERATOR, // The only operator 17 | 18 | // Terminal Nodes 19 | NUMBER, 20 | VARIABLE, 21 | } 22 | -------------------------------------------------------------------------------- /compiler-examples/ck-compiler/src/main/java/com/arjunsk/compiler/ck/domain/tree/ParseTree.java: -------------------------------------------------------------------------------- 1 | package com.arjunsk.compiler.ck.domain.tree; 2 | 3 | import com.arjunsk.compiler.ck.visitor.Visitor; 4 | 5 | /** 6 | * Base for implementing a parse tree. 7 | * 8 | *

Since this is the domain object which will have custom business logic in future, we use {@link 9 | * Visitable#accept(Visitor)} to implement the same. 10 | * 11 | *

You might be wondering, in the {@link 12 | * com.arjunsk.compiler.ck.domain.tree.nodes.grammer.ParserRuleContext} we already have the 13 | * parent-child relationship. Then how is this class helpful. We use this class for generic 14 | * traversal of parent-child paths. Mainly used to propagate accept() and generate toString() of the 15 | * tree. 16 | */ 17 | public interface ParseTree extends Visitable { 18 | 19 | ParseTree getParent(); 20 | 21 | void setParent(ParseTree parent); 22 | 23 | /** 24 | * @return concatenation of all the children.getText() or return the Token value for Terminal 25 | * nodes. 26 | */ 27 | String getText(); 28 | 29 | /** @return this Object or Token entry. */ 30 | Object getPayload(); 31 | 32 | /** Add child to the tree. */ 33 | void addChild(ParseTree child); 34 | 35 | /** Get child at the given index. */ 36 | ParseTree getChild(int i); 37 | 38 | /** @return count of children */ 39 | int getChildCount(); 40 | 41 | /** @return bracket matched flattened string of the tree. */ 42 | String toStringTree(); 43 | } 44 | -------------------------------------------------------------------------------- /compiler-examples/ck-compiler/src/main/java/com/arjunsk/compiler/ck/domain/tree/Visitable.java: -------------------------------------------------------------------------------- 1 | package com.arjunsk.compiler.ck.domain.tree; 2 | 3 | import com.arjunsk.compiler.ck.visitor.Visitor; 4 | 5 | /** visitor pattern, 'Visitable' allows business logic to be separated from domain entities. */ 6 | public interface Visitable { 7 | 8 | /** 9 | * @param visitor Base Visitor or Custom Visitor Implementation. 10 | * @param Output Type Generic present in the Visitor Implementation. 11 | * @return the output based on the datatype passed in Visitor implementation 12 | *

Eg:- public class SimplerLangCustomVisitor extends SimplerLangBaseVisitor 13 | *

Here T = Void, visitor is of type SimplerLangBaseVisitor 14 | */ 15 | T accept(Visitor visitor); 16 | } 17 | -------------------------------------------------------------------------------- /compiler-examples/ck-compiler/src/main/java/com/arjunsk/compiler/ck/domain/tree/nodes/common/TerminalNode.java: -------------------------------------------------------------------------------- 1 | package com.arjunsk.compiler.ck.domain.tree.nodes.common; 2 | 3 | import com.arjunsk.compiler.ck.domain.token.Token; 4 | import com.arjunsk.compiler.ck.domain.tree.ParseTree; 5 | import com.arjunsk.compiler.ck.visitor.Visitor; 6 | 7 | /** 8 | * Terminal node or Leaf node. 9 | * 10 | *

Eg:- VAR or INT 11 | */ 12 | public class TerminalNode implements ParseTree { 13 | 14 | public ParseTree parent; 15 | 16 | public Token symbol; 17 | 18 | @Override 19 | public ParseTree getParent() { 20 | return this.parent; 21 | } 22 | 23 | @Override 24 | public void setParent(ParseTree parent) { 25 | this.parent = parent; 26 | } 27 | 28 | /** 29 | * Terminal nodes will have Token as the payload. (ie VAR or INT) 30 | * 31 | * @param symbol Token value for the terminal node 32 | */ 33 | public void setSymbol(Token symbol) { 34 | this.symbol = symbol; 35 | } 36 | 37 | /** 38 | * Terminal node will have Token as the payload. (ie VAR or INT). We have it as Object return type 39 | * because for ContextNodes, it can be that node itself. 40 | * 41 | * @return Token value. 42 | */ 43 | @Override 44 | public Object getPayload() { 45 | return this.symbol; 46 | } 47 | 48 | /** 49 | * Terminal node will have the Text as Token value. 50 | * 51 | * @return Token Value 52 | */ 53 | @Override 54 | public String getText() { 55 | return this.symbol.getValue(); 56 | } 57 | 58 | /** {@inheritDoc} */ 59 | @Override 60 | public void addChild(ParseTree child) {} 61 | 62 | /** {@inheritDoc} */ 63 | @Override 64 | public ParseTree getChild(int i) { 65 | return null; 66 | } 67 | 68 | /** {@inheritDoc} */ 69 | @Override 70 | public int getChildCount() { 71 | return 0; 72 | } 73 | 74 | /** 75 | * Implementation of toStringTree for terminal node return only the Token Value. 76 | * 77 | * @return Token Value 78 | */ 79 | @Override 80 | public String toStringTree() { 81 | return getText(); 82 | } 83 | 84 | /** 85 | * {@inheritDoc} 86 | * 87 | *

Invoke visitTerminal function in the Visitor Implementation. 88 | */ 89 | @Override 90 | public T accept(Visitor visitor) { 91 | return visitor.visitTerminal(this); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /compiler-examples/ck-compiler/src/main/java/com/arjunsk/compiler/ck/domain/tree/nodes/grammer/ParserRuleContext.java: -------------------------------------------------------------------------------- 1 | package com.arjunsk.compiler.ck.domain.tree.nodes.grammer; 2 | 3 | import com.arjunsk.compiler.ck.domain.tree.ParseTree; 4 | import com.arjunsk.compiler.ck.visitor.Visitor; 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | /** Base implementation for our Grammar Elements. */ 9 | public class ParserRuleContext implements ParseTree { 10 | 11 | public ParseTree parent; 12 | 13 | public List children; 14 | 15 | /** {@inheritDoc} */ 16 | @Override 17 | public ParseTree getParent() { 18 | return this.parent; 19 | } 20 | 21 | /** {@inheritDoc} */ 22 | @Override 23 | public void setParent(ParseTree parent) { 24 | this.parent = parent; 25 | } 26 | 27 | /** {@inheritDoc} */ 28 | @Override 29 | public String getText() { 30 | if (getChildCount() == 0) { 31 | return ""; 32 | } 33 | 34 | StringBuilder builder = new StringBuilder(); 35 | for (int i = 0; i < getChildCount(); i++) { 36 | builder.append(getChild(i).getText()); 37 | } 38 | 39 | return builder.toString(); 40 | } 41 | 42 | /** {@inheritDoc} */ 43 | @Override 44 | public Object getPayload() { 45 | return this; 46 | } 47 | 48 | /** {@inheritDoc} */ 49 | public void addChild(ParseTree child) { 50 | child.setParent(this); 51 | if (children == null) children = new ArrayList<>(); 52 | children.add(child); 53 | } 54 | 55 | /** {@inheritDoc} */ 56 | @Override 57 | public ParseTree getChild(int i) { 58 | return this.children.get(i); 59 | } 60 | 61 | /** {@inheritDoc} */ 62 | @Override 63 | public int getChildCount() { 64 | return children != null ? children.size() : 0; 65 | } 66 | 67 | /** {@inheritDoc} */ 68 | @Override 69 | public String toStringTree() { 70 | if (getChildCount() == 0) { 71 | return ""; 72 | } 73 | 74 | StringBuilder sb = new StringBuilder(); 75 | 76 | sb.append("( "); 77 | sb.append(this.getClass().getSimpleName()); 78 | sb.append("("); 79 | for (int i = 0; i < getChildCount(); i++) { 80 | sb.append(" ").append(getChild(i).toStringTree()).append(" "); 81 | } 82 | sb.append(")"); 83 | sb.append(" )"); 84 | 85 | return sb.toString(); 86 | } 87 | 88 | /** To be overridden in child implementations. */ 89 | @Override 90 | public T accept(Visitor visitor) { 91 | return null; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /compiler-examples/ck-compiler/src/main/java/com/arjunsk/compiler/ck/domain/tree/nodes/grammer/impl/LetContext.java: -------------------------------------------------------------------------------- 1 | package com.arjunsk.compiler.ck.domain.tree.nodes.grammer.impl; 2 | 3 | import com.arjunsk.compiler.ck.domain.tree.nodes.common.TerminalNode; 4 | import com.arjunsk.compiler.ck.domain.tree.nodes.grammer.ParserRuleContext; 5 | import com.arjunsk.compiler.ck.visitor.Visitor; 6 | 7 | /** 8 | * `Let` Syntax ParseRuleContext. 9 | * 10 | *

Eg:- a = 10 ( VAR = INT) 11 | */ 12 | public class LetContext extends ParserRuleContext { 13 | 14 | private final TerminalNode variableName; 15 | 16 | private final TerminalNode variableValue; 17 | 18 | public LetContext(TerminalNode variableName, TerminalNode variableValue) { 19 | this.variableName = variableName; 20 | this.variableValue = variableValue; 21 | 22 | // Add the arguments as children to this node. 23 | this.addChild(variableName); 24 | this.addChild(variableValue); 25 | } 26 | 27 | public TerminalNode getVariableName() { 28 | return variableName; 29 | } 30 | 31 | public TerminalNode getVariableValue() { 32 | return variableValue; 33 | } 34 | 35 | @Override 36 | public T accept(Visitor visitor) { 37 | return visitor.visitLet(this); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /compiler-examples/ck-compiler/src/main/java/com/arjunsk/compiler/ck/domain/tree/nodes/grammer/impl/ProgramContext.java: -------------------------------------------------------------------------------- 1 | package com.arjunsk.compiler.ck.domain.tree.nodes.grammer.impl; 2 | 3 | import com.arjunsk.compiler.ck.domain.tree.nodes.grammer.ParserRuleContext; 4 | import com.arjunsk.compiler.ck.visitor.Visitor; 5 | import java.util.List; 6 | 7 | /** 8 | * `Program` Syntax ParseRuleContext. 9 | * 10 | *

Eg:- Statement+ // ie program consists of multiple statements. 11 | * 12 | *

NOTE: This is a simple compiler. Ideally we should be having the `package name` and `Class 13 | * Name` as part of ProgramContext. This `package name` and `Class Name` could be later used in 14 | * {@link 15 | * com.arjunsk.compiler.ck.visitor.codegenerator.CodeGeneratorVisitor#visitProgram(ProgramContext)} 16 | * to write the corresponding `.class` in the correct `target package` with the correct `class 17 | * name`. 18 | */ 19 | public class ProgramContext extends ParserRuleContext { 20 | 21 | private final List statements; 22 | 23 | public ProgramContext(List statements) { 24 | this.statements = statements; 25 | 26 | // Add the statements as children to this node. 27 | for (StatementContext statement : statements) { 28 | this.addChild(statement); 29 | } 30 | } 31 | 32 | public List getStatements() { 33 | return statements; 34 | } 35 | 36 | public StatementContext getStatements(int i) { 37 | return statements.get(i); 38 | } 39 | 40 | @Override 41 | public T accept(Visitor visitor) { 42 | return visitor.visitProgram(this); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /compiler-examples/ck-compiler/src/main/java/com/arjunsk/compiler/ck/domain/tree/nodes/grammer/impl/ShowContext.java: -------------------------------------------------------------------------------- 1 | package com.arjunsk.compiler.ck.domain.tree.nodes.grammer.impl; 2 | 3 | import com.arjunsk.compiler.ck.domain.tree.nodes.common.TerminalNode; 4 | import com.arjunsk.compiler.ck.domain.tree.nodes.grammer.ParserRuleContext; 5 | import com.arjunsk.compiler.ck.visitor.Visitor; 6 | 7 | /** 8 | * `Show` Syntax ParseRuleContext. 9 | * 10 | *

Eg:- show INT or show VAR 11 | */ 12 | public class ShowContext extends ParserRuleContext { 13 | 14 | private final TerminalNode integerValue; 15 | 16 | private final TerminalNode variableName; 17 | 18 | public ShowContext(TerminalNode integerValue, TerminalNode variableName) { 19 | this.integerValue = integerValue; 20 | this.variableName = variableName; 21 | 22 | // Conditionally add child node 23 | if (integerValue != null) { 24 | this.addChild(integerValue); 25 | } else { 26 | this.addChild(variableName); 27 | } 28 | } 29 | 30 | public TerminalNode getIntegerValue() { 31 | return integerValue; 32 | } 33 | 34 | public TerminalNode getVariableName() { 35 | return variableName; 36 | } 37 | 38 | @Override 39 | public T accept(Visitor visitor) { 40 | return visitor.visitShow(this); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /compiler-examples/ck-compiler/src/main/java/com/arjunsk/compiler/ck/domain/tree/nodes/grammer/impl/StatementContext.java: -------------------------------------------------------------------------------- 1 | package com.arjunsk.compiler.ck.domain.tree.nodes.grammer.impl; 2 | 3 | import com.arjunsk.compiler.ck.domain.tree.nodes.grammer.ParserRuleContext; 4 | import com.arjunsk.compiler.ck.visitor.Visitor; 5 | 6 | /** 7 | * `Statement` Syntax ParseRuleContext. 8 | * 9 | *

Eg:- LET | SHOW // ie either Let or Show statement would be the value of statement. 10 | */ 11 | public class StatementContext extends ParserRuleContext { 12 | 13 | private final LetContext letContext; 14 | 15 | private final ShowContext showContext; 16 | 17 | public StatementContext(LetContext letContext, ShowContext showContext) { 18 | this.letContext = letContext; 19 | this.showContext = showContext; 20 | 21 | // Conditionally add child node 22 | if (letContext != null) { 23 | this.addChild(letContext); 24 | } else { 25 | this.addChild(showContext); 26 | } 27 | } 28 | 29 | public LetContext getLetContext() { 30 | return letContext; 31 | } 32 | 33 | public ShowContext getShowContext() { 34 | return showContext; 35 | } 36 | 37 | @Override 38 | public T accept(Visitor visitor) { 39 | return visitor.visitStatement(this); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /compiler-examples/ck-compiler/src/main/java/com/arjunsk/compiler/ck/exceptions/LexerException.java: -------------------------------------------------------------------------------- 1 | package com.arjunsk.compiler.ck.exceptions; 2 | 3 | public class LexerException extends RuntimeException { 4 | 5 | public LexerException() { 6 | super("Exception While Lexical Tokenizing"); 7 | } 8 | 9 | public LexerException(String message) { 10 | super(message); 11 | } 12 | 13 | public LexerException(String message, Throwable cause) { 14 | super(message, cause); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /compiler-examples/ck-compiler/src/main/java/com/arjunsk/compiler/ck/exceptions/ParserException.java: -------------------------------------------------------------------------------- 1 | package com.arjunsk.compiler.ck.exceptions; 2 | 3 | public class ParserException extends RuntimeException { 4 | 5 | public ParserException() { 6 | super("Exception While Parsing"); 7 | } 8 | 9 | public ParserException(String message) { 10 | super(message); 11 | } 12 | 13 | public ParserException(String message, Throwable cause) { 14 | super(message, cause); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /compiler-examples/ck-compiler/src/main/java/com/arjunsk/compiler/ck/exceptions/SemanticException.java: -------------------------------------------------------------------------------- 1 | package com.arjunsk.compiler.ck.exceptions; 2 | 3 | public class SemanticException extends RuntimeException { 4 | 5 | public SemanticException() { 6 | super("Exception While performing semantic checks."); 7 | } 8 | 9 | public SemanticException(String message) { 10 | super(message); 11 | } 12 | 13 | public SemanticException(String message, Throwable cause) { 14 | super(message, cause); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /compiler-examples/ck-compiler/src/main/java/com/arjunsk/compiler/ck/lexer/Lexer.java: -------------------------------------------------------------------------------- 1 | package com.arjunsk.compiler.ck.lexer; 2 | 3 | import com.arjunsk.compiler.ck.domain.token.Token; 4 | import com.arjunsk.compiler.ck.domain.token.support.TokenType; 5 | import com.arjunsk.compiler.ck.exceptions.LexerException; 6 | import java.util.Arrays; 7 | 8 | public class Lexer { 9 | 10 | private final String code; 11 | private final int codeLength; 12 | 13 | private int currentIndex; 14 | 15 | private Token currentToken; 16 | private Token previousToken; 17 | 18 | public Lexer(String code) { 19 | this.code = code; 20 | this.currentIndex = 0; 21 | this.codeLength = code.length(); 22 | } 23 | 24 | /** 25 | * Updates currentToken to the next valid Token if it is available. 26 | * 27 | * @return true, if a valid token is available next. 28 | */ 29 | public boolean nextToken() { 30 | 31 | while (!isEndOfCode()) { // while loop is to fetch nextToken, if a skipWS occurs. 32 | 33 | previousToken = currentToken; // in case you need the previous token 34 | 35 | final char currentChar = code.charAt(currentIndex); 36 | 37 | if (Arrays.asList(' ', '\r', '\t', '\n').contains(currentChar)) { // 1. WS 38 | skipWhiteSpace(); 39 | continue; 40 | } else if (currentChar == '=') { // 2. LET 41 | currentToken = new Token(TokenType.EQUALS_OPERATOR); 42 | currentIndex++; 43 | } else if (Character.isDigit(currentChar)) { // 3. INT 44 | currentToken = new Token(TokenType.NUMBER, readNumber()); 45 | } else if (Character.isLetter(currentChar)) { 46 | String variableName = readVariable(); 47 | if (variableName.equalsIgnoreCase("show")) { // 4. SHOW 48 | currentToken = new Token(TokenType.SHOW); 49 | } else { // 5. VAR 50 | currentToken = new Token(TokenType.VARIABLE, variableName); 51 | } 52 | } else { 53 | throw new LexerException("Token not defined."); 54 | } 55 | return true; 56 | } 57 | return false; 58 | } 59 | 60 | /** 61 | * Read Integer as String 62 | * 63 | * @return String value of Integer Number. 64 | */ 65 | private String readNumber() { 66 | StringBuilder sb = new StringBuilder(); 67 | char currentChar = code.charAt(currentIndex); 68 | while (!isEndOfCode() && Character.isDigit(currentChar)) { 69 | sb.append(currentChar); 70 | currentIndex++; 71 | if (isEndOfCode()) break; 72 | currentChar = code.charAt(currentIndex); 73 | } 74 | return sb.toString(); 75 | } 76 | 77 | /** @return String read from current index. */ 78 | private String readVariable() { 79 | StringBuilder sb = new StringBuilder(); 80 | char currentChar = code.charAt(currentIndex); 81 | while (!isEndOfCode() && Character.isLetter(currentChar)) { 82 | sb.append(currentChar); 83 | currentIndex++; 84 | if (isEndOfCode()) break; 85 | currentChar = code.charAt(currentIndex); 86 | } 87 | return sb.toString(); 88 | } 89 | 90 | /** Skip WhiteSpace(WS) */ 91 | private void skipWhiteSpace() { 92 | while (!isEndOfCode()) { 93 | if (Arrays.asList(' ', '\r', '\t', '\n').contains(code.charAt(currentIndex))) { 94 | currentIndex++; 95 | } else { 96 | break; 97 | } 98 | } 99 | } 100 | 101 | /** Check if End of Code is reached. */ 102 | private boolean isEndOfCode() { 103 | return currentIndex >= codeLength; 104 | } 105 | 106 | /** 107 | * Get previous Token. 108 | * 109 | *

NOTE: for SimplerLang grammar we don't have much use of previous token. But it will be 110 | * useful when implementing complex Grammar. 111 | */ 112 | public Token getPreviousToken() { 113 | return previousToken; 114 | } 115 | 116 | /** Get current Token. */ 117 | public Token getCurrentToken() { 118 | return currentToken; 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /compiler-examples/ck-compiler/src/main/java/com/arjunsk/compiler/ck/parser/Parser.java: -------------------------------------------------------------------------------- 1 | package com.arjunsk.compiler.ck.parser; 2 | 3 | import com.arjunsk.compiler.ck.domain.token.Token; 4 | import com.arjunsk.compiler.ck.domain.token.support.TokenType; 5 | import com.arjunsk.compiler.ck.domain.tree.nodes.common.TerminalNode; 6 | import com.arjunsk.compiler.ck.domain.tree.nodes.grammer.impl.LetContext; 7 | import com.arjunsk.compiler.ck.domain.tree.nodes.grammer.impl.ProgramContext; 8 | import com.arjunsk.compiler.ck.domain.tree.nodes.grammer.impl.ShowContext; 9 | import com.arjunsk.compiler.ck.domain.tree.nodes.grammer.impl.StatementContext; 10 | import com.arjunsk.compiler.ck.exceptions.ParserException; 11 | import com.arjunsk.compiler.ck.lexer.Lexer; 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | 15 | public class Parser { 16 | 17 | private final Lexer lexer; 18 | 19 | public Parser(Lexer lexer) { 20 | this.lexer = lexer; 21 | } 22 | 23 | /** 24 | * Parse Logic for Program. 25 | * 26 | *

NOTE: We will use if-else to create a Deterministic Finite Automata (DFA). 27 | */ 28 | public ProgramContext parseProgram() { 29 | List statements = new ArrayList<>(); 30 | do { 31 | statements.add(parseStatement()); 32 | } while (lexer.nextToken()); 33 | return new ProgramContext(statements); 34 | } 35 | 36 | /** Parse Logic for Statement. Creates a LET or SHOW statement based on the Tokens passed. */ 37 | public StatementContext parseStatement() { 38 | 39 | if (lexer.getCurrentToken() == null) { 40 | lexer.nextToken(); // Current Token = LET | SHOW 41 | } 42 | 43 | Token token = lexer.getCurrentToken(); // LET | SHOW 44 | 45 | if (token.getType() == TokenType.VARIABLE) { // LET 46 | return new StatementContext(parseLet(), null); 47 | } else if (token.getType() == TokenType.SHOW) { // SHOW 48 | return new StatementContext(null, parseShow()); 49 | } else { 50 | throw new ParserException("Not of type LET or SHOW."); 51 | } 52 | } 53 | 54 | /** Parse Logic for Let. */ 55 | public LetContext parseLet() { 56 | if (lexer.getCurrentToken() == null) { 57 | lexer.nextToken(); // Current Token = VAR 58 | } 59 | TerminalNode variableNameToken = parseTerminalNode(); // VAR 60 | 61 | lexer.nextToken(); // move to : = 62 | lexer.nextToken(); // move to : INT 63 | 64 | TerminalNode valueToken = parseTerminalNode(); // INT 65 | 66 | return new LetContext(variableNameToken, valueToken); 67 | } 68 | 69 | /** Parse Logic for Show. */ 70 | public ShowContext parseShow() { 71 | 72 | if (lexer.getCurrentToken() == null) { 73 | lexer.nextToken(); // Current Token = SHOW 74 | } 75 | 76 | lexer.nextToken(); // Current Token = VAR | INT 77 | 78 | TerminalNode terminal = parseTerminalNode(); // VAR | INT 79 | final Token token = (Token) terminal.getPayload(); 80 | 81 | if (token.getType() == TokenType.NUMBER) { 82 | return new ShowContext(terminal, null); 83 | } else if (token.getType() == TokenType.VARIABLE) { 84 | return new ShowContext(null, terminal); 85 | } else { 86 | throw new ParserException("Show not preceded with var or int"); 87 | } 88 | } 89 | 90 | /** Parse Logic for Terminal Node. */ 91 | public TerminalNode parseTerminalNode() { 92 | 93 | if (lexer.getCurrentToken() == null) { 94 | lexer.nextToken(); // Current Token = VAR | INT 95 | } 96 | 97 | TerminalNode token = new TerminalNode(); 98 | token.setSymbol(lexer.getCurrentToken()); 99 | return token; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /compiler-examples/ck-compiler/src/main/java/com/arjunsk/compiler/ck/visitor/SimplerLangBaseVisitor.java: -------------------------------------------------------------------------------- 1 | package com.arjunsk.compiler.ck.visitor; 2 | 3 | import com.arjunsk.compiler.ck.domain.tree.ParseTree; 4 | import com.arjunsk.compiler.ck.domain.tree.nodes.common.TerminalNode; 5 | import com.arjunsk.compiler.ck.domain.tree.nodes.grammer.impl.LetContext; 6 | import com.arjunsk.compiler.ck.domain.tree.nodes.grammer.impl.ProgramContext; 7 | import com.arjunsk.compiler.ck.domain.tree.nodes.grammer.impl.ShowContext; 8 | import com.arjunsk.compiler.ck.domain.tree.nodes.grammer.impl.StatementContext; 9 | 10 | public class SimplerLangBaseVisitor implements Visitor { 11 | 12 | @Override 13 | public T visitProgram(ProgramContext context) { 14 | return visitChildren(context); 15 | } 16 | 17 | @Override 18 | public T visitStatement(StatementContext context) { 19 | return visitChildren(context); 20 | } 21 | 22 | @Override 23 | public T visitLet(LetContext context) { 24 | return visitChildren(context); 25 | } 26 | 27 | @Override 28 | public T visitShow(ShowContext context) { 29 | return visitChildren(context); 30 | } 31 | 32 | /** There is no child to propagate. */ 33 | @Override 34 | public T visitTerminal(TerminalNode context) { 35 | return defaultResult(); 36 | } 37 | 38 | /** 39 | * Propagate visitor to the children. 40 | * 41 | *

So when you call invoke something like this 42 | * 43 | *

44 |    *     ParseTree tree = parser.parseProgram();
45 |    *     SimplerLangCustomVisitor visitor = new SimplerLangCustomVisitor();
46 |    *     tree.accept(visitor);
47 |    * 
48 | * 49 | * The accept(visitor) `visitor` is propagated to the children of that tree node. 50 | * 51 | * @param node Visitable implementation 52 | */ 53 | public T visitChildren(ParseTree node) { 54 | T result = defaultResult(); 55 | for (int i = 0; i < node.getChildCount(); i++) { 56 | ParseTree c = node.getChild(i); 57 | result = c.accept(this); 58 | } 59 | 60 | return result; // return the last accept result of the children list. 61 | } 62 | 63 | protected T defaultResult() { 64 | return null; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /compiler-examples/ck-compiler/src/main/java/com/arjunsk/compiler/ck/visitor/Visitor.java: -------------------------------------------------------------------------------- 1 | package com.arjunsk.compiler.ck.visitor; 2 | 3 | import com.arjunsk.compiler.ck.domain.tree.nodes.common.TerminalNode; 4 | import com.arjunsk.compiler.ck.domain.tree.nodes.grammer.impl.LetContext; 5 | import com.arjunsk.compiler.ck.domain.tree.nodes.grammer.impl.ProgramContext; 6 | import com.arjunsk.compiler.ck.domain.tree.nodes.grammer.impl.ShowContext; 7 | import com.arjunsk.compiler.ck.domain.tree.nodes.grammer.impl.StatementContext; 8 | 9 | /** 10 | * Visitor Pattern used for separating Business logic from domain class code. 11 | * 12 | * @param Return type of visitXXX. Could be Void, Boolean etc. 13 | */ 14 | public interface Visitor { 15 | 16 | T visitProgram(ProgramContext context); 17 | 18 | T visitStatement(StatementContext context); 19 | 20 | T visitLet(LetContext context); 21 | 22 | T visitShow(ShowContext context); 23 | 24 | T visitTerminal(TerminalNode context); 25 | } 26 | -------------------------------------------------------------------------------- /compiler-examples/ck-compiler/src/main/java/com/arjunsk/compiler/ck/visitor/codegenerator/CodeGeneratorVisitor.java: -------------------------------------------------------------------------------- 1 | package com.arjunsk.compiler.ck.visitor.codegenerator; 2 | 3 | import static org.objectweb.asm.Opcodes.ACC_PUBLIC; 4 | import static org.objectweb.asm.Opcodes.ACC_STATIC; 5 | import static org.objectweb.asm.Opcodes.ACC_SUPER; 6 | import static org.objectweb.asm.Opcodes.ALOAD; 7 | import static org.objectweb.asm.Opcodes.ASTORE; 8 | import static org.objectweb.asm.Opcodes.BIPUSH; 9 | import static org.objectweb.asm.Opcodes.GETSTATIC; 10 | import static org.objectweb.asm.Opcodes.INVOKESTATIC; 11 | import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL; 12 | import static org.objectweb.asm.Opcodes.V1_8; 13 | 14 | import com.arjunsk.compiler.ck.domain.tree.nodes.grammer.impl.LetContext; 15 | import com.arjunsk.compiler.ck.domain.tree.nodes.grammer.impl.ProgramContext; 16 | import com.arjunsk.compiler.ck.domain.tree.nodes.grammer.impl.ShowContext; 17 | import com.arjunsk.compiler.ck.visitor.SimplerLangBaseVisitor; 18 | import java.io.File; 19 | import java.io.FileOutputStream; 20 | import java.util.HashMap; 21 | import java.util.Map; 22 | import org.objectweb.asm.ClassWriter; 23 | import org.objectweb.asm.MethodVisitor; 24 | import org.objectweb.asm.Type; 25 | 26 | /** 27 | * Visitor that converts AST to .class java byte code. 28 | * 29 | *

NOTE 1: To generate ASM code from Java Class you can use ASMifier. This will help you write 30 | * complex ASM codes. Ref:- @see Java 32 | * ASMifier 33 | * 34 | *

NOTE 2: Ops Code reference: @see Java Ops Code 36 | */ 37 | public class CodeGeneratorVisitor extends SimplerLangBaseVisitor { 38 | 39 | // Class Writer 40 | private final ClassWriter classWriter; 41 | 42 | // For assigning correct variable index from LET to SHOW. 43 | private final Map variableIndexMap; 44 | private int variableIndex; 45 | 46 | // Main Method visitor used across LET and SHOW. 47 | private MethodVisitor mainMethodVisitor; 48 | 49 | public CodeGeneratorVisitor() { 50 | this.classWriter = new ClassWriter(0); 51 | variableIndexMap = new HashMap<>(); 52 | 53 | // Variable0 is reserved for args[] in : `main(String[] var0)` 54 | variableIndex = 1; 55 | } 56 | 57 | /** Called when the program node is visited. The main entry point. */ 58 | @Override 59 | public Void visitProgram(ProgramContext context) { 60 | 61 | /** ASM = CODE : public class CgSample. */ 62 | // BEGIN 1: creates a ClassWriter for the `CgSample.class` public class, 63 | classWriter.visit( 64 | V1_8, // Java 1.8 65 | ACC_PUBLIC + ACC_SUPER, // public static 66 | "CgSample", // Class Name 67 | null, // Generics 68 | "java/lang/Object", // Interface extends Object (Super Class), 69 | null // interface names 70 | ); 71 | 72 | /** ASM = CODE : public static void main(String args[]). */ 73 | // BEGIN 2: creates a MethodVisitor for the 'main' method 74 | mainMethodVisitor = 75 | classWriter.visitMethod( 76 | ACC_PUBLIC + ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null); 77 | 78 | super.visitProgram(context); 79 | 80 | // END 2: Close main() 81 | mainMethodVisitor.visitEnd(); 82 | 83 | // END 1: Close class() 84 | classWriter.visitEnd(); 85 | 86 | byte[] code = classWriter.toByteArray(); 87 | writeToFile(code, "output/CgSample.class"); 88 | 89 | return null; 90 | } 91 | 92 | @Override 93 | public Void visitLet(LetContext context) { 94 | 95 | /** ASM = BIPUSH : Push bytes. */ 96 | int variableIntegerVal = Integer.parseInt(context.getVariableValue().getText()); 97 | mainMethodVisitor.visitIntInsn(BIPUSH, variableIntegerVal); 98 | 99 | /** ASM = CODE : Integer.valueOf( ) . */ 100 | mainMethodVisitor.visitMethodInsn( 101 | INVOKESTATIC, 102 | Type.getType(Integer.class).getInternalName(), 103 | "valueOf", 104 | "(I)Ljava/lang/Integer;", 105 | false); 106 | 107 | /** 108 | * ASM = ASTORE : Store reference into local variable. 109 | * 110 | *

This stores the above valueOf(INT) as Integer Variable. 111 | */ 112 | // STORE to Variable Pool at variableIndex 113 | mainMethodVisitor.visitVarInsn(ASTORE, variableIndex); 114 | 115 | // Saving the variableIndex for reference in SHOW() 116 | variableIndexMap.put(context.getVariableName().getText(), variableIndex); 117 | variableIndex++; 118 | 119 | return null; 120 | } 121 | 122 | @Override 123 | public Void visitShow(ShowContext context) { 124 | 125 | /** ASM = CODE : System.out */ 126 | mainMethodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); 127 | 128 | if (context.getVariableName() != null) { 129 | 130 | /** 131 | * ASM = LOAD Variable: ALOAD variable 132 | * 133 | *

ALOAD: Load reference from local variable 134 | */ 135 | // Fetch index from VariablePool 136 | int index = variableIndexMap.get(context.getVariableName().getText()); 137 | // LOAD from variable pool 138 | mainMethodVisitor.visitVarInsn(ALOAD, index); 139 | 140 | /** ASM = INVOKE: println(Object) with variable loaded via ALOAD. */ 141 | mainMethodVisitor.visitMethodInsn( 142 | INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/Object;)V", false); 143 | } else if (context.getIntegerValue() != null) { 144 | 145 | /** ASM = BIPUSH: Push byte. */ 146 | 147 | // Get integer value of the constant. 148 | int integerVal = Integer.parseInt(context.getIntegerValue().getText()); 149 | // PUSH integerValue 150 | mainMethodVisitor.visitIntInsn(BIPUSH, integerVal); 151 | 152 | /** ASM = INVOKE: println(I) with variable loaded via ALOAD. */ 153 | mainMethodVisitor.visitMethodInsn( 154 | INVOKEVIRTUAL, "java/io/PrintStream", "println", "(I)V", false); 155 | } 156 | return null; 157 | } 158 | 159 | /** 160 | * Writes Byte Array to a file 161 | * 162 | * @param code Byte Array of Source code. 163 | * @param filePath File Path to write. Eg:- "Example.class" 164 | */ 165 | private void writeToFile(byte[] code, String filePath) { 166 | 167 | File file = new File(filePath); 168 | file.getParentFile().mkdirs(); 169 | 170 | try (FileOutputStream fos = new FileOutputStream(filePath)) { 171 | fos.write(code); 172 | } catch (Exception ex) { 173 | ex.printStackTrace(); 174 | } 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /compiler-examples/ck-compiler/src/main/java/com/arjunsk/compiler/ck/visitor/interpreter/InterpreterVisitor.java: -------------------------------------------------------------------------------- 1 | package com.arjunsk.compiler.ck.visitor.interpreter; 2 | 3 | import com.arjunsk.compiler.ck.domain.tree.nodes.grammer.impl.LetContext; 4 | import com.arjunsk.compiler.ck.domain.tree.nodes.grammer.impl.ShowContext; 5 | import com.arjunsk.compiler.ck.visitor.SimplerLangBaseVisitor; 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | /** 10 | * SimplerLang Interpreter Implementation. Interpreter executes code line by line. 11 | * 12 | *

NOTE: Here we write the logic for storing the `let` variable and displaying the `show` output. 13 | */ 14 | public class InterpreterVisitor extends SimplerLangBaseVisitor { 15 | 16 | private final Map variableMap; 17 | 18 | public InterpreterVisitor() { 19 | super(); 20 | variableMap = new HashMap<>(); 21 | } 22 | 23 | @Override 24 | public Void visitLet(LetContext context) { 25 | this.variableMap.put(context.getVariableName().getText(), context.getVariableValue().getText()); 26 | return super.visitLet(context); 27 | } 28 | 29 | @Override 30 | public Void visitShow(ShowContext context) { 31 | if (context.getIntegerValue() != null) { 32 | System.out.println(context.getIntegerValue().getText()); 33 | } else if (context.getVariableName() != null) { 34 | System.out.println(this.variableMap.get(context.getVariableName().getText())); 35 | } 36 | return super.visitShow(context); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /compiler-examples/ck-compiler/src/main/java/com/arjunsk/compiler/ck/visitor/semantic/SemanticAnalyzer.java: -------------------------------------------------------------------------------- 1 | package com.arjunsk.compiler.ck.visitor.semantic; 2 | 3 | import com.arjunsk.compiler.ck.domain.tree.nodes.common.TerminalNode; 4 | import com.arjunsk.compiler.ck.domain.tree.nodes.grammer.impl.LetContext; 5 | import com.arjunsk.compiler.ck.domain.tree.nodes.grammer.impl.ShowContext; 6 | import com.arjunsk.compiler.ck.domain.tree.nodes.grammer.impl.StatementContext; 7 | import com.arjunsk.compiler.ck.exceptions.SemanticException; 8 | import com.arjunsk.compiler.ck.visitor.SimplerLangBaseVisitor; 9 | import java.util.HashMap; 10 | import java.util.Map; 11 | 12 | /** 13 | * `Syntax` is the concept that concerns itself only whether or not the sentence is valid for the 14 | * grammar of the language. `Semantics` is about whether or not the sentence has a valid meaning. 15 | * 16 | *

NOTE: checking whether the variable is declared before "SHOW VAR" is an example of `Semantic` 17 | * check. 18 | */ 19 | public class SemanticAnalyzer extends SimplerLangBaseVisitor { 20 | 21 | private final Map variableMap; 22 | 23 | public SemanticAnalyzer() { 24 | super(); 25 | this.variableMap = new HashMap<>(); 26 | } 27 | 28 | /** Validate Statement Semantics. */ 29 | @Override 30 | public Void visitStatement(StatementContext context) { 31 | if (context.getLetContext() == null && context.getShowContext() == null) { 32 | throw new SemanticException("Statement should of type LET or SHOW."); 33 | } else if (context.getLetContext() != null && context.getShowContext() != null) { 34 | throw new SemanticException("Statement should be either of type LET or SHOW & not both."); 35 | } 36 | 37 | return super.visitStatement(context); 38 | } 39 | 40 | /** Validate LET Semantics. */ 41 | @Override 42 | public Void visitLet(LetContext context) { 43 | 44 | String variableName = context.getVariableName().getText(); 45 | String variableValue = context.getVariableValue().getText(); 46 | 47 | if (variableName == null || variableName.isEmpty()) { 48 | throw new SemanticException("Variable name cannot be empty."); 49 | } else if (variableValue == null || variableValue.isEmpty()) { 50 | throw new SemanticException("Variable value cannot be empty."); 51 | } 52 | 53 | // Check if variable value is Integer. In our case, this will be already handled in the 54 | // Tokenizer. 55 | try { 56 | Integer.parseInt(variableValue); 57 | } catch (NumberFormatException | NullPointerException ex) { 58 | throw new SemanticException("Variable value should be integer.", ex); 59 | } 60 | 61 | // This will be used to check whether variable is declared using LET before invoking SHOW for 62 | // the variable. 63 | variableMap.put(variableName, variableValue); 64 | 65 | return super.visitLet(context); 66 | } 67 | 68 | /** 69 | * Validate SHOW Semantics. 70 | * 71 | *

NOTE: We validate if the variable is previously declared using LET. 72 | */ 73 | @Override 74 | public Void visitShow(ShowContext context) { 75 | 76 | TerminalNode variableNameTN = context.getVariableName(); 77 | TerminalNode integerValueTN = context.getIntegerValue(); 78 | 79 | /* 1. Checking whether either of VAR | INT is present.*/ 80 | boolean isVarPresent = variableNameTN != null && !variableNameTN.getText().isEmpty(); 81 | boolean isIntPresent = integerValueTN != null && !integerValueTN.getText().isEmpty(); 82 | 83 | if (!isVarPresent && !isIntPresent) { 84 | throw new SemanticException("SHOW should have integer or variable as argument"); 85 | } else if (isVarPresent && isIntPresent) { 86 | throw new SemanticException("SHOW should have either integer or variable as argument"); 87 | } 88 | 89 | /* 2. If SHOW Argument is Number, check if it is an integer. In our case, this will be 90 | already handled in the Tokenizer.*/ 91 | if (integerValueTN != null) { 92 | try { 93 | Integer.parseInt(integerValueTN.getText()); 94 | } catch (NumberFormatException | NullPointerException ex) { 95 | throw new SemanticException("SHOW argument is not a valid integer.", ex); 96 | } 97 | } 98 | 99 | /* 3. if SHOW Argument is Variable, check if the variable is declared previously.*/ 100 | if (variableNameTN != null && !variableMap.containsKey(variableNameTN.getText())) { 101 | throw new SemanticException("SHOW argument variable is not declared."); 102 | } 103 | 104 | return super.visitShow(context); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /compiler-examples/ck-compiler/src/test/java/com/arjunsk/compiler/ck/CkCompilerTest.java: -------------------------------------------------------------------------------- 1 | package com.arjunsk.compiler.ck; 2 | 3 | import com.arjunsk.compiler.ck.domain.tree.ParseTree; 4 | import com.arjunsk.compiler.ck.lexer.Lexer; 5 | import com.arjunsk.compiler.ck.parser.Parser; 6 | import com.arjunsk.compiler.ck.visitor.codegenerator.CodeGeneratorVisitor; 7 | import com.arjunsk.compiler.ck.visitor.interpreter.InterpreterVisitor; 8 | import com.arjunsk.compiler.ck.visitor.semantic.SemanticAnalyzer; 9 | import com.arjunsk.compiler.utils.FileReaderUtil; 10 | import org.junit.Assert; 11 | import org.junit.Test; 12 | 13 | public class CkCompilerTest { 14 | 15 | @Test 16 | public void test_e2e() { 17 | // 0. Input Code 18 | final String sourceCode = FileReaderUtil.getResourceFileAsString("input/CgSample.ck"); 19 | 20 | // 1. Lexer 21 | assert sourceCode != null; 22 | Lexer lexer = new Lexer(sourceCode); 23 | 24 | // 2. Parser 25 | Parser parser = new Parser(lexer); 26 | ParseTree tree = parser.parseProgram(); 27 | 28 | Assert.assertNotNull(tree); 29 | 30 | // 3. Semantic Analyzer Visitor 31 | tree.accept(new SemanticAnalyzer()); 32 | 33 | // 4.1 Interpreter Visitor 34 | tree.accept(new InterpreterVisitor()); 35 | 36 | // 4.2 Compiler Visitor 37 | tree.accept(new CodeGeneratorVisitor()); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /compiler-examples/ck-compiler/src/test/java/com/arjunsk/compiler/ck/lexer/LexerTest.java: -------------------------------------------------------------------------------- 1 | package com.arjunsk.compiler.ck.lexer; 2 | 3 | import com.arjunsk.compiler.ck.domain.token.support.TokenType; 4 | import org.junit.Assert; 5 | import org.junit.Test; 6 | 7 | public class LexerTest { 8 | 9 | @Test 10 | public void test_tokenizer() { 11 | 12 | // 1. Arrange 13 | String sourceCode = "a = 10\n" + "show a"; 14 | 15 | // 2. Act 16 | Lexer lexer = new Lexer(sourceCode); 17 | 18 | // 3. Assert 19 | Assert.assertTrue(lexer.nextToken()); 20 | Assert.assertEquals(TokenType.VARIABLE, lexer.getCurrentToken().getType()); 21 | Assert.assertEquals("a", lexer.getCurrentToken().getValue()); 22 | Assert.assertNull(lexer.getPreviousToken()); 23 | 24 | Assert.assertTrue(lexer.nextToken()); 25 | Assert.assertEquals(TokenType.EQUALS_OPERATOR, lexer.getCurrentToken().getType()); 26 | Assert.assertEquals(TokenType.VARIABLE, lexer.getPreviousToken().getType()); 27 | Assert.assertEquals("a", lexer.getPreviousToken().getValue()); 28 | 29 | Assert.assertTrue(lexer.nextToken()); 30 | Assert.assertEquals(TokenType.NUMBER, lexer.getCurrentToken().getType()); 31 | Assert.assertEquals("10", lexer.getCurrentToken().getValue()); 32 | Assert.assertEquals(TokenType.EQUALS_OPERATOR, lexer.getPreviousToken().getType()); 33 | 34 | Assert.assertTrue(lexer.nextToken()); 35 | Assert.assertEquals(TokenType.SHOW, lexer.getCurrentToken().getType()); 36 | Assert.assertEquals(TokenType.NUMBER, lexer.getPreviousToken().getType()); 37 | Assert.assertEquals("10", lexer.getPreviousToken().getValue()); 38 | 39 | Assert.assertTrue(lexer.nextToken()); 40 | Assert.assertEquals(TokenType.VARIABLE, lexer.getCurrentToken().getType()); 41 | Assert.assertEquals("a", lexer.getCurrentToken().getValue()); 42 | Assert.assertEquals(TokenType.SHOW, lexer.getPreviousToken().getType()); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /compiler-examples/ck-compiler/src/test/java/com/arjunsk/compiler/ck/parser/ParserTest.java: -------------------------------------------------------------------------------- 1 | package com.arjunsk.compiler.ck.parser; 2 | 3 | import com.arjunsk.compiler.ck.domain.tree.ParseTree; 4 | import com.arjunsk.compiler.ck.lexer.Lexer; 5 | import org.junit.Assert; 6 | import org.junit.Test; 7 | 8 | public class ParserTest { 9 | 10 | @Test 11 | public void test_parser() { 12 | 13 | // 1. Arrange 14 | String sourceCode = "a = 10\n" + "show a\n" + "show 20"; 15 | Lexer lexer = new Lexer(sourceCode); 16 | 17 | // 2. Act 18 | Parser parser = new Parser(lexer); 19 | ParseTree tree = parser.parseProgram(); 20 | 21 | // 3. Assert 22 | Assert.assertEquals(3, tree.getChildCount()); 23 | Assert.assertEquals(1, tree.getChild(0).getChildCount()); // Statement 24 | Assert.assertEquals(2, tree.getChild(0).getChild(0).getChildCount()); // LET a=10 25 | 26 | Assert.assertEquals(1, tree.getChild(1).getChildCount()); // Statement 27 | Assert.assertEquals(1, tree.getChild(1).getChild(0).getChildCount()); // SHOW show a 28 | 29 | Assert.assertEquals(1, tree.getChild(2).getChildCount()); // Statement 30 | Assert.assertEquals(1, tree.getChild(2).getChild(0).getChildCount()); // SHOW show 23 31 | 32 | Assert.assertEquals( 33 | "( ProgramContext( ( StatementContext( ( LetContext( a 10 ) ) ) ) ( StatementContext( ( ShowContext( a ) ) ) ) ( StatementContext( ( ShowContext( 20 ) ) ) ) ) )", 34 | tree.toStringTree()); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /compiler-examples/ck-compiler/src/test/java/com/arjunsk/compiler/ck/visitor/semantic/SemanticAnalyzerTest.java: -------------------------------------------------------------------------------- 1 | package com.arjunsk.compiler.ck.visitor.semantic; 2 | 3 | import static org.hamcrest.CoreMatchers.startsWith; 4 | 5 | import com.arjunsk.compiler.ck.domain.tree.ParseTree; 6 | import com.arjunsk.compiler.ck.exceptions.SemanticException; 7 | import com.arjunsk.compiler.ck.lexer.Lexer; 8 | import com.arjunsk.compiler.ck.parser.Parser; 9 | import org.junit.Rule; 10 | import org.junit.Test; 11 | import org.junit.rules.ExpectedException; 12 | 13 | public class SemanticAnalyzerTest { 14 | 15 | @Rule public final ExpectedException exceptionCatcher = ExpectedException.none(); 16 | 17 | @Test 18 | public void test_variable_not_declared() { 19 | // 1. Arrange 20 | String sourceCode = "a = 10\n" + "show b"; 21 | Lexer lexer = new Lexer(sourceCode); 22 | 23 | Parser parser = new Parser(lexer); 24 | ParseTree tree = parser.parseProgram(); 25 | 26 | // 3. Assert 27 | exceptionCatcher.expect(SemanticException.class); 28 | exceptionCatcher.expectMessage(startsWith("SHOW argument variable is not declared.")); 29 | 30 | // 2. Act 31 | tree.accept(new SemanticAnalyzer()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /compiler-examples/ck-compiler/src/test/java/com/arjunsk/compiler/utils/FileReaderUtil.java: -------------------------------------------------------------------------------- 1 | package com.arjunsk.compiler.utils; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.InputStream; 5 | import java.io.InputStreamReader; 6 | import java.util.stream.Collectors; 7 | 8 | /** File Reader for reading files from test resource. */ 9 | public class FileReaderUtil { 10 | 11 | /** 12 | * Reads given resource file as a string. 13 | * 14 | * @param fileName path to the resource file 15 | * @return the file's content in String 16 | */ 17 | public static String getResourceFileAsString(String fileName) { 18 | ClassLoader classLoader = ClassLoader.getSystemClassLoader(); 19 | try (InputStream is = classLoader.getResourceAsStream(fileName)) { 20 | if (is == null) return null; 21 | try (InputStreamReader isr = new InputStreamReader(is); 22 | BufferedReader reader = new BufferedReader(isr)) { 23 | return reader.lines().collect(Collectors.joining(System.lineSeparator())); 24 | } 25 | } catch (Exception ex) { 26 | throw new RuntimeException("Error reading file", ex); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /compiler-examples/ck-compiler/src/test/resources/input/CgSample.ck: -------------------------------------------------------------------------------- 1 | a = 10 2 | show a 3 | show 20 4 | b = 30 5 | show b 6 | show a -------------------------------------------------------------------------------- /compiler-examples/lisp-compiler/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | com.arjunsk 9 | compiler-examples 10 | 1.0-SNAPSHOT 11 | 12 | 13 | lisp-compiler 14 | 15 | 16 | -------------------------------------------------------------------------------- /compiler-examples/lisp-compiler/src/main/java/com/arjunsk/compiler/lisp/core/emitter/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dborchard/tiny-compiler/cda17b3ac617e8270c3f1358635f8052a012165a/compiler-examples/lisp-compiler/src/main/java/com/arjunsk/compiler/lisp/core/emitter/.gitkeep -------------------------------------------------------------------------------- /compiler-examples/lisp-compiler/src/main/java/com/arjunsk/compiler/lisp/core/lexer/Lexer.java: -------------------------------------------------------------------------------- 1 | package com.arjunsk.compiler.lisp.core.lexer; 2 | 3 | import com.arjunsk.compiler.lisp.domain.lexer.Token; 4 | import com.arjunsk.compiler.lisp.domain.lexer.support.TokenType; 5 | import com.arjunsk.compiler.lisp.exceptions.LexerException; 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | public class Lexer { 10 | 11 | /** 12 | * Convert code to token stream. 13 | * 14 | * @param input High Level Code 15 | * @return List of tokens 16 | */ 17 | public List tokenize(String input) { 18 | List result = new ArrayList<>(); 19 | 20 | int currentIndex = 0; 21 | char currentChar; 22 | 23 | while (currentIndex < input.length()) { 24 | 25 | currentChar = input.charAt(currentIndex); 26 | 27 | if (currentChar == '(' || currentChar == ')') { 28 | result.add(new Token(TokenType.PAREN, currentChar + "")); 29 | currentIndex++; 30 | } else if (currentChar == '\n' || currentChar == '\r' || currentChar == ' ') { 31 | currentIndex++; 32 | } else if (currentChar == '"') { 33 | 34 | // Iterate till ending double quote. 35 | currentChar = input.charAt(++currentIndex); 36 | StringBuilder value = new StringBuilder(); 37 | while (currentChar != '"') { 38 | value.append(currentChar); 39 | currentChar = input.charAt(++currentIndex); 40 | } 41 | result.add(new Token(TokenType.STRING, value.toString())); 42 | } else if (Character.isDigit(currentChar)) { 43 | 44 | // Iterate till last digit of the number 45 | StringBuilder value = new StringBuilder(); 46 | while (Character.isDigit(currentChar)) { 47 | value.append(currentChar); 48 | currentChar = input.charAt(++currentIndex); 49 | } 50 | result.add(new Token(TokenType.NUMBER, value.toString())); 51 | } else if (Character.isLetter(currentChar)) { 52 | 53 | // Iterate till last character of the word. 54 | StringBuilder value = new StringBuilder(); 55 | while (Character.isLetter(currentChar)) { 56 | value.append(currentChar); 57 | currentChar = input.charAt(++currentIndex); 58 | } 59 | result.add(new Token(TokenType.NAME, value.toString())); 60 | } else { 61 | throw new LexerException(); 62 | } 63 | } 64 | 65 | return result; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /compiler-examples/lisp-compiler/src/main/java/com/arjunsk/compiler/lisp/core/parser/Parser.java: -------------------------------------------------------------------------------- 1 | package com.arjunsk.compiler.lisp.core.parser; 2 | 3 | import com.arjunsk.compiler.lisp.domain.ast.AstNode; 4 | import com.arjunsk.compiler.lisp.domain.lexer.Token; 5 | import com.arjunsk.compiler.lisp.domain.lexer.support.TokenType; 6 | import com.arjunsk.compiler.lisp.exceptions.ParserException; 7 | import java.util.List; 8 | 9 | public class Parser { 10 | 11 | private final List tokens; 12 | 13 | private int current = 0; 14 | 15 | public Parser(List tokens) { 16 | this.tokens = tokens; 17 | } 18 | 19 | public AstNode parse() { 20 | AstNode root = new AstNode("Program"); 21 | 22 | while (current < tokens.size()) { 23 | root.appendParams(walk()); 24 | } 25 | 26 | return root; 27 | } 28 | 29 | private AstNode walk() { 30 | Token token = tokens.get(current); 31 | 32 | if (token.getType() == TokenType.NUMBER) { // 1. Number 33 | current++; 34 | return new AstNode("NumberLiteral", token.getValue()); 35 | } else if (token.getType() == TokenType.STRING) { // 2. Variable Name 36 | current++; 37 | return new AstNode("StringLiteral", token.getValue()); 38 | } else if (token.getType() == TokenType.PAREN 39 | && token.getValue().equals("(")) { // 3. Expression 40 | 41 | token = tokens.get(++current); 42 | final String expressionName = token.getValue(); 43 | 44 | // Base Node 45 | AstNode node = new AstNode("CallExpression", expressionName); 46 | 47 | token = tokens.get(++current); 48 | 49 | while ((token.getType() != TokenType.PAREN) 50 | || (token.getType() == TokenType.PAREN && !token.getValue().equals(")"))) { 51 | 52 | node.appendParams(walk()); 53 | token = tokens.get(current); 54 | } 55 | current++; 56 | return node; 57 | } else { 58 | throw new ParserException(); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /compiler-examples/lisp-compiler/src/main/java/com/arjunsk/compiler/lisp/core/transformer/Transformer.java: -------------------------------------------------------------------------------- 1 | package com.arjunsk.compiler.lisp.core.transformer; 2 | 3 | import com.arjunsk.compiler.lisp.domain.ast.AstNode; 4 | import com.arjunsk.compiler.lisp.domain.ast.LispAstNode; 5 | 6 | // TODO: Continue from here 7 | public class Transformer { 8 | 9 | public LispAstNode transform(AstNode root) { 10 | LispAstNode newAst = new LispAstNode("Program"); 11 | return newAst; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /compiler-examples/lisp-compiler/src/main/java/com/arjunsk/compiler/lisp/core/visitor/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dborchard/tiny-compiler/cda17b3ac617e8270c3f1358635f8052a012165a/compiler-examples/lisp-compiler/src/main/java/com/arjunsk/compiler/lisp/core/visitor/.gitkeep -------------------------------------------------------------------------------- /compiler-examples/lisp-compiler/src/main/java/com/arjunsk/compiler/lisp/domain/ast/AstNode.java: -------------------------------------------------------------------------------- 1 | package com.arjunsk.compiler.lisp.domain.ast; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Iterator; 5 | import java.util.List; 6 | 7 | /** Input AST. */ 8 | public class AstNode { 9 | 10 | private final String type; // CallExpression or NumberLiteral or StringLiteral 11 | 12 | private final String value; // Node Value 13 | 14 | private final List params; 15 | 16 | public AstNode(String type) { 17 | this.type = type; 18 | this.value = ""; 19 | this.params = new ArrayList<>(); 20 | } 21 | 22 | public AstNode(String type, String value) { 23 | this.type = type; 24 | this.value = value; 25 | this.params = new ArrayList<>(); 26 | } 27 | 28 | public void appendParams(AstNode node) { 29 | this.params.add(node); 30 | } 31 | 32 | public String getType() { 33 | return type; 34 | } 35 | 36 | public String getValue() { 37 | return value; 38 | } 39 | 40 | public Iterator getParams() { 41 | return this.params.iterator(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /compiler-examples/lisp-compiler/src/main/java/com/arjunsk/compiler/lisp/domain/ast/LispAstNode.java: -------------------------------------------------------------------------------- 1 | package com.arjunsk.compiler.lisp.domain.ast; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Iterator; 5 | import java.util.List; 6 | 7 | /** Transformed AST */ 8 | public class LispAstNode { 9 | 10 | private final String type; 11 | 12 | private final String value; 13 | 14 | private final List arguments; 15 | 16 | public LispAstNode(String type) { 17 | this.type = type; 18 | this.value = ""; 19 | this.arguments = new ArrayList<>(); 20 | } 21 | 22 | public LispAstNode(String type, String value) { 23 | this.type = type; 24 | this.value = value; 25 | this.arguments = new ArrayList<>(); 26 | } 27 | 28 | public void appendParams(LispAstNode node) { 29 | this.arguments.add(node); 30 | } 31 | 32 | public String getType() { 33 | return type; 34 | } 35 | 36 | public String getValue() { 37 | return value; 38 | } 39 | 40 | public Iterator getArguments() { 41 | return this.arguments.iterator(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /compiler-examples/lisp-compiler/src/main/java/com/arjunsk/compiler/lisp/domain/lexer/Token.java: -------------------------------------------------------------------------------- 1 | package com.arjunsk.compiler.lisp.domain.lexer; 2 | 3 | import com.arjunsk.compiler.lisp.domain.lexer.support.TokenType; 4 | 5 | public class Token { 6 | 7 | private final TokenType type; 8 | 9 | private final String value; 10 | 11 | public Token(TokenType type, String value) { 12 | this.type = type; 13 | this.value = value; 14 | } 15 | 16 | public TokenType getType() { 17 | return type; 18 | } 19 | 20 | public String getValue() { 21 | return value; 22 | } 23 | 24 | @Override 25 | public String toString() { 26 | return this.type + " " + this.value; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /compiler-examples/lisp-compiler/src/main/java/com/arjunsk/compiler/lisp/domain/lexer/support/TokenType.java: -------------------------------------------------------------------------------- 1 | package com.arjunsk.compiler.lisp.domain.lexer.support; 2 | 3 | public enum TokenType { 4 | PAREN, 5 | NUMBER, 6 | STRING, 7 | NAME 8 | } 9 | -------------------------------------------------------------------------------- /compiler-examples/lisp-compiler/src/main/java/com/arjunsk/compiler/lisp/exceptions/LexerException.java: -------------------------------------------------------------------------------- 1 | package com.arjunsk.compiler.lisp.exceptions; 2 | 3 | public class LexerException extends RuntimeException { 4 | 5 | public LexerException() { 6 | super("Exception in Lexer"); 7 | } 8 | 9 | public LexerException(String message) { 10 | super(message); 11 | } 12 | 13 | public LexerException(String message, Throwable cause) { 14 | super(message, cause); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /compiler-examples/lisp-compiler/src/main/java/com/arjunsk/compiler/lisp/exceptions/ParserException.java: -------------------------------------------------------------------------------- 1 | package com.arjunsk.compiler.lisp.exceptions; 2 | 3 | public class ParserException extends RuntimeException { 4 | 5 | public ParserException() { 6 | super("Exception in Parsing"); 7 | } 8 | 9 | public ParserException(String message) { 10 | super(message); 11 | } 12 | 13 | public ParserException(String message, Throwable cause) { 14 | super(message, cause); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /compiler-examples/lisp-compiler/src/test/java/com/arjunsk/compiler/lisp/core/lexer/LexerTest.java: -------------------------------------------------------------------------------- 1 | package com.arjunsk.compiler.lisp.core.lexer; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import com.arjunsk.compiler.lisp.domain.lexer.Token; 6 | import com.arjunsk.compiler.utils.FileReaderUtil; 7 | import java.util.List; 8 | import java.util.Objects; 9 | import org.junit.Test; 10 | 11 | public class LexerTest { 12 | 13 | @Test 14 | public void test_tokenize() { 15 | 16 | // 1. Arrange 17 | String hllCode = 18 | Objects.requireNonNull(FileReaderUtil.getResourceFileAsString("input/code.lsp")); 19 | 20 | // 2. Act 21 | List tokens = new Lexer().tokenize(hllCode); 22 | 23 | // 3. Assert 24 | assertEquals(9, tokens.size()); 25 | 26 | // 4. Log 27 | tokens.forEach(System.out::println); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /compiler-examples/lisp-compiler/src/test/java/com/arjunsk/compiler/lisp/core/parser/ParserTest.java: -------------------------------------------------------------------------------- 1 | package com.arjunsk.compiler.lisp.core.parser; 2 | 3 | import static org.junit.Assert.assertNotNull; 4 | 5 | import com.arjunsk.compiler.lisp.core.lexer.Lexer; 6 | import com.arjunsk.compiler.lisp.domain.ast.AstNode; 7 | import com.arjunsk.compiler.lisp.domain.lexer.Token; 8 | import com.arjunsk.compiler.utils.FileReaderUtil; 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | import java.util.Objects; 12 | import org.junit.Test; 13 | 14 | public class ParserTest { 15 | 16 | @Test 17 | public void test_parse() { 18 | 19 | // 1. Arrange 20 | String hllCode = 21 | Objects.requireNonNull(FileReaderUtil.getResourceFileAsString("input/code.lsp")); 22 | List tokens = new Lexer().tokenize(hllCode); 23 | 24 | // 2. Act 25 | AstNode root = new Parser(tokens).parse(); 26 | 27 | // 3. Assert 28 | assertNotNull(root); 29 | 30 | // 4. Log 31 | System.out.println(printAst(root)); 32 | } 33 | 34 | private String printAst(AstNode root) { 35 | 36 | if (root == null) return ""; 37 | 38 | StringBuilder sb = new StringBuilder(); 39 | sb.append(" Type: ") 40 | .append(root.getType()) 41 | .append(" Value: ") 42 | .append(root.getValue()) 43 | .append("\n"); 44 | 45 | List children = new ArrayList<>(); 46 | root.getParams() 47 | .forEachRemaining( 48 | item -> { 49 | String child = printAst(item); 50 | if (!child.isEmpty()) children.add(child); 51 | }); 52 | if (!children.isEmpty()) { 53 | sb.append("[").append("\n"); 54 | children.forEach(sb::append); 55 | sb.append("]").append("\n"); 56 | } 57 | 58 | return sb.toString(); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /compiler-examples/lisp-compiler/src/test/java/com/arjunsk/compiler/utils/FileReaderUtil.java: -------------------------------------------------------------------------------- 1 | package com.arjunsk.compiler.utils; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.InputStream; 5 | import java.io.InputStreamReader; 6 | import java.util.stream.Collectors; 7 | 8 | /** File Reader for reading files from test resource. */ 9 | public class FileReaderUtil { 10 | 11 | /** 12 | * Reads given resource file as a string. 13 | * 14 | * @param fileName path to the resource file 15 | * @return the file's content in String 16 | */ 17 | public static String getResourceFileAsString(String fileName) { 18 | ClassLoader classLoader = ClassLoader.getSystemClassLoader(); 19 | try (InputStream is = classLoader.getResourceAsStream(fileName)) { 20 | if (is == null) return null; 21 | try (InputStreamReader isr = new InputStreamReader(is); 22 | BufferedReader reader = new BufferedReader(isr)) { 23 | return reader.lines().collect(Collectors.joining(System.lineSeparator())); 24 | } 25 | } catch (Exception ex) { 26 | throw new RuntimeException("Error reading file", ex); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /compiler-examples/lisp-compiler/src/test/resources/input/code.lsp: -------------------------------------------------------------------------------- 1 | (add 2 (subtract 4 2)) -------------------------------------------------------------------------------- /compiler-examples/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | com.arjunsk 9 | codekrypt-compiler 10 | 1.0-SNAPSHOT 11 | 12 | 13 | compiler-examples 14 | pom 15 | 16 | 17 | svg-compiler 18 | lisp-compiler 19 | ck-compiler 20 | 21 | 22 | -------------------------------------------------------------------------------- /compiler-examples/svg-compiler/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | com.arjunsk 9 | compiler-examples 10 | 1.0-SNAPSHOT 11 | 12 | 13 | svg-compiler 14 | 15 | 16 | -------------------------------------------------------------------------------- /compiler-examples/svg-compiler/src/main/java/com/arjunsk/compiler/svg/core/generation/CodeGenerator.java: -------------------------------------------------------------------------------- 1 | package com.arjunsk.compiler.svg.core.generation; 2 | 3 | import com.arjunsk.compiler.svg.domain.transformer.ast.SvgAstNode; 4 | 5 | /** Convert to SVG based on Transformed AST. */ 6 | public class CodeGenerator { 7 | 8 | public String generate(SvgAstNode node) { 9 | return printAst(node); 10 | } 11 | 12 | /** using recursion to process transformed AST. */ 13 | private String printAst(SvgAstNode root) { 14 | 15 | if (root == null) return ""; 16 | 17 | StringBuilder sb = new StringBuilder(); 18 | 19 | sb.append("<").append(root.getTag()); 20 | 21 | root.getAttributes() 22 | .forEachRemaining( 23 | item -> 24 | sb.append(" ") 25 | .append(item.getKey()) 26 | .append("=") 27 | .append("\"") 28 | .append(item.getValue()) 29 | .append("\"")); 30 | 31 | sb.append(">"); 32 | 33 | root.getBody().forEachRemaining(item -> sb.append("\n").append(printAst(item))); 34 | 35 | sb.append("\n").append(""); 36 | 37 | return sb.toString(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /compiler-examples/svg-compiler/src/main/java/com/arjunsk/compiler/svg/core/parsing/Lexer.java: -------------------------------------------------------------------------------- 1 | package com.arjunsk.compiler.svg.core.parsing; 2 | 3 | import com.arjunsk.compiler.svg.domain.lexer.token.Token; 4 | import com.arjunsk.compiler.svg.domain.lexer.token.support.TokenType; 5 | import com.arjunsk.compiler.svg.exceptions.LexerException; 6 | import java.util.Arrays; 7 | import java.util.List; 8 | import java.util.stream.Collectors; 9 | 10 | /* 11 | 12 | Input: 13 | Paper 100 14 | 15 | Output: 16 | WORD Paper 17 | NUMBER 100 18 | 19 | */ 20 | 21 | /** Convert Input Code to Token's. */ 22 | public final class Lexer { 23 | 24 | private Lexer() {} 25 | 26 | public static List tokenize(String hllCode) { 27 | 28 | return Arrays.stream(hllCode.split("\\s+")) 29 | .filter(element -> !element.trim().isEmpty()) 30 | .map( 31 | element -> { 32 | char firstChar = element.charAt(0); 33 | if (Character.isLetter(firstChar)) { 34 | return new Token(TokenType.WORD, element); 35 | } else if (isNumeric(element)) { 36 | return new Token(TokenType.NUMBER, element); 37 | } else { 38 | throw new LexerException(); 39 | } 40 | }) 41 | .collect(Collectors.toList()); 42 | } 43 | 44 | private static boolean isNumeric(String num) { 45 | return num.chars().allMatch(Character::isDigit); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /compiler-examples/svg-compiler/src/main/java/com/arjunsk/compiler/svg/core/parsing/Parser.java: -------------------------------------------------------------------------------- 1 | package com.arjunsk.compiler.svg.core.parsing; 2 | 3 | import com.arjunsk.compiler.svg.domain.lexer.ast.AstNode; 4 | import com.arjunsk.compiler.svg.domain.lexer.ast.support.AstNodeClass; 5 | import com.arjunsk.compiler.svg.domain.lexer.token.Token; 6 | import com.arjunsk.compiler.svg.domain.lexer.token.support.TokenType; 7 | import com.arjunsk.compiler.svg.exceptions.ParserException; 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | public class Parser { 12 | 13 | private final List tokens; 14 | 15 | private int currentTokenIndex; 16 | 17 | public Parser(List tokens) { 18 | this.tokens = tokens; 19 | this.currentTokenIndex = 0; 20 | } 21 | 22 | public AstNode parse() { 23 | 24 | AstNode ast = new AstNode(AstNodeClass.BLOCK); 25 | 26 | boolean isPaperDefined = false; 27 | boolean isPenDefined = false; 28 | 29 | while (currentTokenIndex < tokens.size()) { 30 | Token currentToken = tokens.get(currentTokenIndex); 31 | 32 | if (currentToken.getType() == TokenType.WORD) { 33 | AstNode expression; 34 | 35 | switch (currentToken.getValue()) { 36 | case "Paper": 37 | if (isPaperDefined) throw new ParserException("Paper already defined"); 38 | 39 | expression = new AstNode(AstNodeClass.CALL_EXPRESSION, "Paper"); 40 | findNumericalArguments(1).forEach(expression::appendNode); 41 | ast.appendNode(expression); 42 | isPaperDefined = true; 43 | break; 44 | case "Pen": 45 | if (isPenDefined) throw new ParserException("Pen already defined"); 46 | 47 | expression = new AstNode(AstNodeClass.CALL_EXPRESSION, "Pen"); 48 | findNumericalArguments(1).forEach(expression::appendNode); 49 | ast.appendNode(expression); 50 | isPenDefined = true; 51 | break; 52 | case "Line": 53 | if (!(isPaperDefined && isPenDefined)) 54 | throw new ParserException("No Paper & Pen defined"); 55 | 56 | expression = new AstNode(AstNodeClass.CALL_EXPRESSION, "Line"); 57 | findNumericalArguments(4).forEach(expression::appendNode); 58 | ast.appendNode(expression); 59 | break; 60 | default: 61 | throw new ParserException("Invalid Token"); 62 | } 63 | } else { 64 | throw new ParserException("Unexpected Token Type" + currentToken.getType()); 65 | } 66 | currentTokenIndex = currentTokenIndex + 1; 67 | } 68 | return ast; 69 | } 70 | 71 | private List findNumericalArguments(int argCount) { 72 | List result = new ArrayList<>(); 73 | while (argCount-- > 0) { 74 | result.add(new AstNode(AstNodeClass.NUMBER, tokens.get(++currentTokenIndex).getValue())); 75 | } 76 | return result; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /compiler-examples/svg-compiler/src/main/java/com/arjunsk/compiler/svg/core/transforming/Transformer.java: -------------------------------------------------------------------------------- 1 | package com.arjunsk.compiler.svg.core.transforming; 2 | 3 | import com.arjunsk.compiler.svg.domain.lexer.ast.AstNode; 4 | import com.arjunsk.compiler.svg.domain.lexer.ast.support.AstNodeClass; 5 | import com.arjunsk.compiler.svg.domain.transformer.ast.SvgAstNode; 6 | import com.arjunsk.compiler.svg.exceptions.TransformerException; 7 | import java.util.Iterator; 8 | 9 | /** Transform the AST to SVG AST. */ 10 | public class Transformer { 11 | 12 | public SvgAstNode transform(AstNode oldAst) { 13 | 14 | SvgAstNode newAst = new SvgAstNode("svg"); 15 | newAst.appendAttributes("width", 100); 16 | newAst.appendAttributes("height", 100); 17 | newAst.appendAttributes("viewBox", "0 0 100 100"); 18 | newAst.appendAttributes("xmlns", "http://www.w3.org/2000/svg"); 19 | newAst.appendAttributes("version", "1.1"); 20 | 21 | AstNode currentAstNode; 22 | String penColor = "89"; 23 | SvgAstNode expression; 24 | 25 | final Iterator children = oldAst.getChildren(); 26 | while (children.hasNext()) { 27 | currentAstNode = children.next(); 28 | if (currentAstNode.getNodeClass() == AstNodeClass.CALL_EXPRESSION) { 29 | switch (currentAstNode.getValue()) { 30 | case "Paper": 31 | expression = new SvgAstNode("rect"); 32 | expression.appendAttributes("x", 0); 33 | expression.appendAttributes("y", 0); 34 | expression.appendAttributes("width", 100); 35 | expression.appendAttributes("height", 100); 36 | expression.appendAttributes( 37 | "fill", makeColor(currentAstNode.getChildren().next().getValue())); 38 | newAst.appendBody(expression); 39 | break; 40 | case "Pen": 41 | penColor = currentAstNode.getChildren().next().getValue(); 42 | break; 43 | case "Line": 44 | expression = new SvgAstNode("line"); 45 | final Iterator lineArgs = currentAstNode.getChildren(); 46 | expression.appendAttributes("x1", Integer.parseInt(lineArgs.next().getValue())); 47 | expression.appendAttributes("y1", Integer.parseInt(lineArgs.next().getValue())); 48 | expression.appendAttributes("x2", Integer.parseInt(lineArgs.next().getValue())); 49 | expression.appendAttributes("y2", Integer.parseInt(lineArgs.next().getValue())); 50 | expression.appendAttributes("stroke", makeColor(penColor)); 51 | expression.appendAttributes("stroke-linecap", "round"); 52 | newAst.appendBody(expression); 53 | break; 54 | default: 55 | throw new TransformerException("Invalid Token"); 56 | } 57 | } 58 | } 59 | 60 | return newAst; 61 | } 62 | 63 | private String makeColor(String level) { 64 | int output = 100 - Integer.parseInt(level); 65 | return "rgb(" + output + "%, " + output + "%, " + output + "%)"; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /compiler-examples/svg-compiler/src/main/java/com/arjunsk/compiler/svg/domain/lexer/ast/AstNode.java: -------------------------------------------------------------------------------- 1 | package com.arjunsk.compiler.svg.domain.lexer.ast; 2 | 3 | import com.arjunsk.compiler.svg.domain.lexer.ast.support.AstNodeClass; 4 | import java.util.ArrayList; 5 | import java.util.Iterator; 6 | import java.util.List; 7 | 8 | public class AstNode { 9 | 10 | private final AstNodeClass nodeClass; 11 | 12 | private final String value; 13 | 14 | private final List children = new ArrayList<>(); 15 | 16 | public AstNode(AstNodeClass nodeClass, String value) { 17 | this.nodeClass = nodeClass; 18 | this.value = value; 19 | } 20 | 21 | public AstNode(AstNodeClass nodeClass) { 22 | this.nodeClass = nodeClass; 23 | this.value = ""; 24 | } 25 | 26 | public AstNodeClass getNodeClass() { 27 | return nodeClass; 28 | } 29 | 30 | public String getValue() { 31 | return value; 32 | } 33 | 34 | public Iterator getChildren() { 35 | return this.children.iterator(); 36 | } 37 | 38 | public void appendNode(AstNode astNode) { 39 | this.children.add(astNode); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /compiler-examples/svg-compiler/src/main/java/com/arjunsk/compiler/svg/domain/lexer/ast/support/AstNodeClass.java: -------------------------------------------------------------------------------- 1 | package com.arjunsk.compiler.svg.domain.lexer.ast.support; 2 | 3 | public enum AstNodeClass { 4 | BLOCK, 5 | CALL_EXPRESSION, 6 | NUMBER 7 | } 8 | -------------------------------------------------------------------------------- /compiler-examples/svg-compiler/src/main/java/com/arjunsk/compiler/svg/domain/lexer/token/Token.java: -------------------------------------------------------------------------------- 1 | package com.arjunsk.compiler.svg.domain.lexer.token; 2 | 3 | import com.arjunsk.compiler.svg.domain.lexer.token.support.TokenType; 4 | 5 | public class Token { 6 | 7 | private final TokenType type; 8 | 9 | private final String value; 10 | 11 | public Token(TokenType type, String value) { 12 | this.type = type; 13 | this.value = value; 14 | } 15 | 16 | public TokenType getType() { 17 | return type; 18 | } 19 | 20 | public String getValue() { 21 | return value; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /compiler-examples/svg-compiler/src/main/java/com/arjunsk/compiler/svg/domain/lexer/token/support/TokenType.java: -------------------------------------------------------------------------------- 1 | package com.arjunsk.compiler.svg.domain.lexer.token.support; 2 | 3 | public enum TokenType { 4 | WORD, 5 | NUMBER 6 | } 7 | -------------------------------------------------------------------------------- /compiler-examples/svg-compiler/src/main/java/com/arjunsk/compiler/svg/domain/transformer/ast/SvgAstNode.java: -------------------------------------------------------------------------------- 1 | package com.arjunsk.compiler.svg.domain.transformer.ast; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashMap; 5 | import java.util.Iterator; 6 | import java.util.List; 7 | import java.util.Map; 8 | import java.util.Map.Entry; 9 | 10 | public class SvgAstNode { 11 | 12 | private final String tag; 13 | private final Map attributes; 14 | private final List body; 15 | 16 | public SvgAstNode(String tag) { 17 | this.tag = tag; 18 | this.attributes = new HashMap<>(); 19 | this.body = new ArrayList<>(); 20 | } 21 | 22 | public void appendBody(SvgAstNode node) { 23 | this.body.add(node); 24 | } 25 | 26 | public void appendAttributes(String key, Object value) { 27 | attributes.put(key, value); 28 | } 29 | 30 | public String getTag() { 31 | return tag; 32 | } 33 | 34 | public Iterator> getAttributes() { 35 | return attributes.entrySet().iterator(); 36 | } 37 | 38 | public Iterator getBody() { 39 | return body.iterator(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /compiler-examples/svg-compiler/src/main/java/com/arjunsk/compiler/svg/exceptions/LexerException.java: -------------------------------------------------------------------------------- 1 | package com.arjunsk.compiler.svg.exceptions; 2 | 3 | public class LexerException extends RuntimeException { 4 | 5 | public LexerException() { 6 | super("Exception While Lexical Tokenizing"); 7 | } 8 | 9 | public LexerException(String message, Throwable cause) { 10 | super(message, cause); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /compiler-examples/svg-compiler/src/main/java/com/arjunsk/compiler/svg/exceptions/ParserException.java: -------------------------------------------------------------------------------- 1 | package com.arjunsk.compiler.svg.exceptions; 2 | 3 | public class ParserException extends RuntimeException { 4 | 5 | public ParserException() { 6 | super("Exception While Parsing"); 7 | } 8 | 9 | public ParserException(String message) { 10 | super(message); 11 | } 12 | 13 | public ParserException(String message, Throwable cause) { 14 | super(message, cause); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /compiler-examples/svg-compiler/src/main/java/com/arjunsk/compiler/svg/exceptions/TransformerException.java: -------------------------------------------------------------------------------- 1 | package com.arjunsk.compiler.svg.exceptions; 2 | 3 | public class TransformerException extends RuntimeException { 4 | 5 | public TransformerException() { 6 | super("Exception While Transforming"); 7 | } 8 | 9 | public TransformerException(String message) { 10 | super(message); 11 | } 12 | 13 | public TransformerException(String message, Throwable cause) { 14 | super(message, cause); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /compiler-examples/svg-compiler/src/test/java/com/arjunsk/compiler/svg/core/generation/CodeGeneratorTest.java: -------------------------------------------------------------------------------- 1 | package com.arjunsk.compiler.svg.core.generation; 2 | 3 | import static org.junit.Assert.assertNotNull; 4 | 5 | import com.arjunsk.compiler.svg.core.parsing.Lexer; 6 | import com.arjunsk.compiler.svg.core.parsing.Parser; 7 | import com.arjunsk.compiler.svg.core.transforming.Transformer; 8 | import com.arjunsk.compiler.svg.domain.lexer.ast.AstNode; 9 | import com.arjunsk.compiler.svg.domain.lexer.token.Token; 10 | import com.arjunsk.compiler.svg.domain.transformer.ast.SvgAstNode; 11 | import com.arjunsk.compiler.utils.FileReaderUtil; 12 | import java.io.FileWriter; 13 | import java.util.List; 14 | import java.util.Objects; 15 | import org.junit.Test; 16 | 17 | public class CodeGeneratorTest { 18 | 19 | @Test 20 | public void test_generate() { 21 | 22 | // 1. Arrange 23 | String hllCode = 24 | Objects.requireNonNull(FileReaderUtil.getResourceFileAsString("lexer/code.ck")); 25 | List tokens = Lexer.tokenize(hllCode); 26 | AstNode oldAst = new Parser(tokens).parse(); 27 | SvgAstNode newAst = new Transformer().transform(oldAst); 28 | 29 | // 2. Act 30 | String result = new CodeGenerator().generate(newAst); 31 | 32 | // 3. Assert 33 | assertNotNull(result); 34 | 35 | // 4. Log 36 | System.out.println(result); 37 | saveToFile(result, "src/test/resources/output/code.svg"); 38 | } 39 | 40 | private void saveToFile(String data, String filename) { 41 | try (FileWriter fw = new FileWriter(filename)) { 42 | fw.write(data); 43 | } catch (Exception e) { 44 | throw new RuntimeException(e); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /compiler-examples/svg-compiler/src/test/java/com/arjunsk/compiler/svg/core/parsing/LexerTest.java: -------------------------------------------------------------------------------- 1 | package com.arjunsk.compiler.svg.core.parsing; 2 | 3 | import com.arjunsk.compiler.svg.domain.lexer.token.Token; 4 | import com.arjunsk.compiler.utils.FileReaderUtil; 5 | import java.util.List; 6 | import java.util.Objects; 7 | import org.junit.Assert; 8 | import org.junit.Test; 9 | 10 | public class LexerTest { 11 | 12 | @Test 13 | public void test_tokenize() { 14 | // 1. Arrange 15 | String hllCode = 16 | Objects.requireNonNull(FileReaderUtil.getResourceFileAsString("lexer/code.ck")); 17 | 18 | // 2. Act 19 | List resultList = Lexer.tokenize(hllCode); 20 | 21 | // 3. Assert 22 | Assert.assertEquals(19, resultList.size()); 23 | 24 | // (Optional) 4. Log 25 | resultList.forEach(token -> System.out.println(token.getType() + " " + token.getValue())); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /compiler-examples/svg-compiler/src/test/java/com/arjunsk/compiler/svg/core/parsing/ParserTest.java: -------------------------------------------------------------------------------- 1 | package com.arjunsk.compiler.svg.core.parsing; 2 | 3 | import com.arjunsk.compiler.svg.domain.lexer.ast.AstNode; 4 | import com.arjunsk.compiler.svg.domain.lexer.token.Token; 5 | import com.arjunsk.compiler.utils.FileReaderUtil; 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | import java.util.Objects; 9 | import org.junit.Assert; 10 | import org.junit.Test; 11 | 12 | public class ParserTest { 13 | 14 | @Test 15 | public void test_parse() { 16 | 17 | // 1. Arrange 18 | String hllCode = Objects.requireNonNull(FileReaderUtil.getResourceFileAsString("lexer/code.ck")); 19 | List tokens = Lexer.tokenize(hllCode); 20 | 21 | // 2. Act 22 | AstNode result = new Parser(tokens).parse(); 23 | 24 | // 3. Assert 25 | Assert.assertNotNull(result); 26 | 27 | // 4. Log 28 | System.out.println(printAst(result)); 29 | } 30 | 31 | private String printAst(AstNode root) { 32 | 33 | if (root == null) return ""; 34 | 35 | StringBuilder sb = new StringBuilder(); 36 | sb.append(" Type: ") 37 | .append(root.getNodeClass()) 38 | .append(" Value: ") 39 | .append(root.getValue()) 40 | .append("\n"); 41 | 42 | List children = new ArrayList<>(); 43 | root.getChildren() 44 | .forEachRemaining( 45 | item -> { 46 | String child = printAst(item); 47 | if (!child.isEmpty()) children.add(child); 48 | }); 49 | if (!children.isEmpty()) { 50 | sb.append("[").append("\n"); 51 | children.forEach(sb::append); 52 | sb.append("]").append("\n"); 53 | } 54 | 55 | return sb.toString(); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /compiler-examples/svg-compiler/src/test/java/com/arjunsk/compiler/svg/core/transforming/TransformerTest.java: -------------------------------------------------------------------------------- 1 | package com.arjunsk.compiler.svg.core.transforming; 2 | 3 | import com.arjunsk.compiler.svg.core.parsing.Lexer; 4 | import com.arjunsk.compiler.svg.core.parsing.Parser; 5 | import com.arjunsk.compiler.svg.domain.lexer.ast.AstNode; 6 | import com.arjunsk.compiler.svg.domain.lexer.token.Token; 7 | import com.arjunsk.compiler.svg.domain.transformer.ast.SvgAstNode; 8 | import com.arjunsk.compiler.utils.FileReaderUtil; 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | import java.util.Objects; 12 | import org.junit.Assert; 13 | import org.junit.Test; 14 | 15 | public class TransformerTest { 16 | 17 | @Test 18 | public void test_transform() { 19 | // 1. Arrange 20 | String hllCode = 21 | Objects.requireNonNull(FileReaderUtil.getResourceFileAsString("lexer/code.ck")); 22 | List tokens = Lexer.tokenize(hllCode); 23 | AstNode oldAst = new Parser(tokens).parse(); 24 | 25 | // 2. Act 26 | SvgAstNode result = new Transformer().transform(oldAst); 27 | 28 | // 3. Assert 29 | Assert.assertNotNull(result); 30 | 31 | // 4. Log 32 | System.out.println(printAst(result)); 33 | } 34 | 35 | private String printAst(SvgAstNode root) { 36 | 37 | if (root == null) return ""; 38 | 39 | StringBuilder sb = new StringBuilder(); 40 | sb.append(root.getTag()).append("\n"); 41 | 42 | root.getAttributes() 43 | .forEachRemaining( 44 | item -> sb.append(item.getKey()).append(" --> ").append(item.getValue()).append("\n")); 45 | 46 | sb.append("\n"); 47 | 48 | List children = new ArrayList<>(); 49 | root.getBody() 50 | .forEachRemaining( 51 | item -> { 52 | String child = printAst(item); 53 | if (!child.isEmpty()) children.add(child); 54 | }); 55 | if (!children.isEmpty()) { 56 | sb.append("[").append("\n"); 57 | children.forEach(sb::append); 58 | sb.append("]").append("\n"); 59 | } 60 | 61 | return sb.toString(); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /compiler-examples/svg-compiler/src/test/java/com/arjunsk/compiler/utils/FileReaderUtil.java: -------------------------------------------------------------------------------- 1 | package com.arjunsk.compiler.utils; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.InputStream; 5 | import java.io.InputStreamReader; 6 | import java.util.stream.Collectors; 7 | 8 | /** File Reader for reading files from test resource. */ 9 | public class FileReaderUtil { 10 | 11 | /** 12 | * Reads given resource file as a string. 13 | * 14 | * @param fileName path to the resource file 15 | * @return the file's content in String 16 | */ 17 | public static String getResourceFileAsString(String fileName) { 18 | ClassLoader classLoader = ClassLoader.getSystemClassLoader(); 19 | try (InputStream is = classLoader.getResourceAsStream(fileName)) { 20 | if (is == null) return null; 21 | try (InputStreamReader isr = new InputStreamReader(is); 22 | BufferedReader reader = new BufferedReader(isr)) { 23 | return reader.lines().collect(Collectors.joining(System.lineSeparator())); 24 | } 25 | } catch (Exception ex) { 26 | throw new RuntimeException("Error reading file", ex); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /compiler-examples/svg-compiler/src/test/resources/lexer/code.ck: -------------------------------------------------------------------------------- 1 | Paper 100 2 | Pen 0 3 | Line 50 77 22 27 4 | Line 22 27 78 27 5 | Line 78 27 50 77 -------------------------------------------------------------------------------- /compiler-examples/svg-compiler/src/test/resources/output/code.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /parser-examples/antlr-examples/antlr-listener-example/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | com.arjunsk 9 | antlr-examples 10 | 1.0-SNAPSHOT 11 | 12 | 13 | antlr-listener-example 14 | 15 | 16 | 17 | 18 | 19 | org.apache.maven.plugins 20 | maven-compiler-plugin 21 | ${maven-compiler-plugin.version} 22 | 23 | ${java.version} 24 | ${java.version} 25 | 26 | 27 | 28 | 29 | org.antlr 30 | antlr4-maven-plugin 31 | ${antlr4.version} 32 | 33 | 34 | 35 | antlr4 36 | 37 | 38 | true 39 | false 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /parser-examples/antlr-examples/antlr-listener-example/src/main/antlr4/com/arjunsk/parser/antlr/SimplerLang.g4: -------------------------------------------------------------------------------- 1 | grammar SimplerLang; 2 | 3 | program : statement+; 4 | 5 | statement : let | show ; 6 | 7 | let : VAR '=' INT ; 8 | show : 'show' (INT | VAR) ; 9 | 10 | VAR : ('a'..'z'|'A'..'Z')+ ; 11 | INT : '0'..'9'+ ; 12 | WS : [ \n\t]+ -> skip; 13 | -------------------------------------------------------------------------------- /parser-examples/antlr-examples/antlr-listener-example/src/main/java/com/arjunsk/parser/antlr/listener/AntlrListenerDriver.java: -------------------------------------------------------------------------------- 1 | package com.arjunsk.parser.antlr.listener; 2 | 3 | import com.arjunsk.parser.antlr.SimplerLangLexer; 4 | import com.arjunsk.parser.antlr.SimplerLangParser; 5 | import com.arjunsk.parser.antlr.listener.support.SimplerLangListenerImpl; 6 | import org.antlr.v4.runtime.CharStreams; 7 | import org.antlr.v4.runtime.CommonTokenStream; 8 | 9 | public class AntlrListenerDriver { 10 | 11 | public static void main(String[] args) { 12 | String code = 13 | "a = 100\n" 14 | + "b = 150\n" 15 | + "show 10\n" 16 | + "show a\n" 17 | + "show b\n" 18 | + "show 121\n" 19 | + "show 200"; 20 | 21 | compile(code); 22 | } 23 | 24 | private static void compile(String source) { 25 | 26 | SimplerLangLexer lexer = new SimplerLangLexer(CharStreams.fromString(source)); 27 | SimplerLangParser parser = new SimplerLangParser(new CommonTokenStream(lexer)); 28 | 29 | parser.addParseListener(new SimplerLangListenerImpl()); 30 | parser.program(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /parser-examples/antlr-examples/antlr-listener-example/src/main/java/com/arjunsk/parser/antlr/listener/support/SimplerLangListenerImpl.java: -------------------------------------------------------------------------------- 1 | package com.arjunsk.parser.antlr.listener.support; 2 | 3 | import com.arjunsk.parser.antlr.SimplerLangBaseListener; 4 | import com.arjunsk.parser.antlr.SimplerLangParser.LetContext; 5 | import com.arjunsk.parser.antlr.SimplerLangParser.ShowContext; 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | /** Logic for handling LET and SHOW. */ 10 | public class SimplerLangListenerImpl extends SimplerLangBaseListener { 11 | 12 | private final Map variableMap; 13 | 14 | public SimplerLangListenerImpl() { 15 | super(); 16 | this.variableMap = new HashMap<>(); 17 | } 18 | 19 | @Override 20 | public void exitLet(LetContext ctx) { 21 | this.variableMap.put(ctx.VAR().getText(), Integer.parseInt(ctx.INT().getText())); 22 | super.exitLet(ctx); 23 | } 24 | 25 | @Override 26 | public void exitShow(ShowContext ctx) { 27 | if (ctx.INT() != null) { 28 | System.out.println(ctx.INT().getText()); 29 | } else if (ctx.VAR() != null) { 30 | System.out.println(this.variableMap.get(ctx.VAR().getText())); 31 | } 32 | super.exitShow(ctx); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /parser-examples/antlr-examples/antlr-visitor-example1/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | com.arjunsk 9 | antlr-examples 10 | 1.0-SNAPSHOT 11 | 12 | 13 | antlr-visitor-example1 14 | 15 | 16 | 17 | 18 | 19 | org.apache.maven.plugins 20 | maven-compiler-plugin 21 | ${maven-compiler-plugin.version} 22 | 23 | ${java.version} 24 | ${java.version} 25 | 26 | 27 | 28 | 29 | org.antlr 30 | antlr4-maven-plugin 31 | ${antlr4.version} 32 | 33 | 34 | 35 | antlr4 36 | 37 | 38 | false 39 | true 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /parser-examples/antlr-examples/antlr-visitor-example1/src/main/antlr4/com/arjunsk/parser/antlr/Calculator.g4: -------------------------------------------------------------------------------- 1 | grammar Calculator; 2 | 3 | operation : left=NUMBER operator='+' right=NUMBER 4 | | left=NUMBER operator='-' right=NUMBER 5 | | left=NUMBER operator='*' right=NUMBER 6 | | left=NUMBER operator='/' right=NUMBER; 7 | 8 | NUMBER 9 | : ('0' .. '9') + ('.' ('0' .. '9') +)? ; 10 | 11 | WS : (' ' | '\t')+ -> channel(HIDDEN); -------------------------------------------------------------------------------- /parser-examples/antlr-examples/antlr-visitor-example1/src/main/java/com/arjunsk/parser/antlr/visitor/AntlrVisitorDriver1.java: -------------------------------------------------------------------------------- 1 | package com.arjunsk.parser.antlr.visitor; 2 | 3 | import com.arjunsk.parser.antlr.CalculatorLexer; 4 | import com.arjunsk.parser.antlr.CalculatorParser; 5 | import com.arjunsk.parser.antlr.visitor.support.CalculatorVisitorImpl; 6 | import org.antlr.v4.runtime.CharStreams; 7 | import org.antlr.v4.runtime.CommonTokenStream; 8 | import org.antlr.v4.runtime.tree.ParseTree; 9 | 10 | public class AntlrVisitorDriver1 { 11 | 12 | public static void main(String[] args) { 13 | 14 | AntlrVisitorDriver1 driver = new AntlrVisitorDriver1(); 15 | 16 | System.out.println(driver.compile("2 + 5")); // 7.0 17 | System.out.println(driver.compile("2 * 5")); // 10.0 18 | System.out.println(driver.compile("5 - 3")); // 2.0 19 | System.out.println(driver.compile("5 / 3")); // 1.6666666666666667 20 | } 21 | 22 | private Double compile(String source) { 23 | 24 | CalculatorLexer lexer = new CalculatorLexer(CharStreams.fromString(source)); 25 | CalculatorParser parser = new CalculatorParser(new CommonTokenStream(lexer)); 26 | 27 | ParseTree tree = parser.operation(); 28 | 29 | CalculatorVisitorImpl visitor = new CalculatorVisitorImpl(); 30 | return tree.accept(visitor); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /parser-examples/antlr-examples/antlr-visitor-example1/src/main/java/com/arjunsk/parser/antlr/visitor/support/CalculatorVisitorImpl.java: -------------------------------------------------------------------------------- 1 | package com.arjunsk.parser.antlr.visitor.support; 2 | 3 | import com.arjunsk.parser.antlr.CalculatorBaseVisitor; 4 | import com.arjunsk.parser.antlr.CalculatorParser; 5 | 6 | /** Logic for handling NUMERIC Operations. */ 7 | public class CalculatorVisitorImpl extends CalculatorBaseVisitor { 8 | 9 | @Override 10 | public Double visitOperation(CalculatorParser.OperationContext ctx) { 11 | if (ctx.operator == null) { 12 | throw new UnsupportedOperationException( 13 | "An operator of +, -, /, * is required to perform the operation"); 14 | } 15 | String operator = ctx.operator.getText(); 16 | double left = Double.parseDouble(ctx.left.getText()); 17 | double right = Double.parseDouble(ctx.right.getText()); 18 | 19 | switch (operator) { 20 | case "+": 21 | return left + right; 22 | case "-": 23 | return left - right; 24 | case "/": 25 | return left / right; 26 | case "*": 27 | return left * right; 28 | default: 29 | throw new UnsupportedOperationException("Calculator does not support " + operator); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /parser-examples/antlr-examples/antlr-visitor.example2/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | com.arjunsk 9 | antlr-examples 10 | 1.0-SNAPSHOT 11 | 12 | 13 | antlr-visitor.example2 14 | 15 | 16 | 17 | 18 | 19 | org.apache.maven.plugins 20 | maven-compiler-plugin 21 | ${maven-compiler-plugin.version} 22 | 23 | ${java.version} 24 | ${java.version} 25 | 26 | 27 | 28 | 29 | org.antlr 30 | antlr4-maven-plugin 31 | ${antlr4.version} 32 | 33 | 34 | 35 | antlr4 36 | 37 | 38 | false 39 | true 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /parser-examples/antlr-examples/antlr-visitor.example2/src/main/antlr4/com/arjunsk/parser/antlr/SimplerLang.g4: -------------------------------------------------------------------------------- 1 | grammar SimplerLang; 2 | 3 | program : statement+; 4 | 5 | statement : let | show ; 6 | 7 | let : VAR '=' INT ; 8 | show : 'show' (INT | VAR) ; 9 | 10 | VAR : ('a'..'z'|'A'..'Z')+ ; 11 | INT : '0'..'9'+ ; 12 | WS : [ \n\t]+ -> skip; 13 | -------------------------------------------------------------------------------- /parser-examples/antlr-examples/antlr-visitor.example2/src/main/java/com/arjunsk/parser/antlr/listener/AntlrVisitorDriver2.java: -------------------------------------------------------------------------------- 1 | package com.arjunsk.parser.antlr.listener; 2 | 3 | import com.arjunsk.parser.antlr.SimplerLangLexer; 4 | import com.arjunsk.parser.antlr.SimplerLangParser; 5 | import com.arjunsk.parser.antlr.listener.support.SimplerLangVisitorImpl; 6 | import org.antlr.v4.runtime.CharStreams; 7 | import org.antlr.v4.runtime.CommonTokenStream; 8 | import org.antlr.v4.runtime.tree.ParseTree; 9 | 10 | public class AntlrVisitorDriver2 { 11 | 12 | public static void main(String[] args) { 13 | String code = 14 | "a = 100\n" 15 | + "b = 150\n" 16 | + "show 10\n" 17 | + "show a\n" 18 | + "show b\n" 19 | + "show 121\n" 20 | + "show 200"; 21 | 22 | compile(code); 23 | } 24 | 25 | private static void compile(String source) { 26 | 27 | SimplerLangLexer lexer = new SimplerLangLexer(CharStreams.fromString(source)); 28 | SimplerLangParser parser = new SimplerLangParser(new CommonTokenStream(lexer)); 29 | 30 | ParseTree tree = parser.program(); 31 | SimplerLangVisitorImpl visitor = new SimplerLangVisitorImpl(); 32 | tree.accept(visitor); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /parser-examples/antlr-examples/antlr-visitor.example2/src/main/java/com/arjunsk/parser/antlr/listener/support/SimplerLangVisitorImpl.java: -------------------------------------------------------------------------------- 1 | package com.arjunsk.parser.antlr.listener.support; 2 | 3 | import com.arjunsk.parser.antlr.SimplerLangBaseVisitor; 4 | import com.arjunsk.parser.antlr.SimplerLangParser.LetContext; 5 | import com.arjunsk.parser.antlr.SimplerLangParser.ShowContext; 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | /** Logic for handling LET and SHOW. */ 10 | public class SimplerLangVisitorImpl extends SimplerLangBaseVisitor { 11 | 12 | private final Map variableMap; 13 | 14 | public SimplerLangVisitorImpl() { 15 | super(); 16 | variableMap = new HashMap<>(); 17 | } 18 | 19 | @Override 20 | public Void visitLet(LetContext ctx) { 21 | this.variableMap.put(ctx.VAR().getText(), Integer.parseInt(ctx.INT().getText())); 22 | return super.visitLet(ctx); 23 | } 24 | 25 | @Override 26 | public Void visitShow(ShowContext ctx) { 27 | if (ctx.INT() != null) { 28 | System.out.println(ctx.INT().getText()); 29 | } else if (ctx.VAR() != null) { 30 | System.out.println(this.variableMap.get(ctx.VAR().getText())); 31 | } 32 | 33 | return super.visitShow(ctx); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /parser-examples/antlr-examples/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | com.arjunsk 9 | parser-examples 10 | 1.0-SNAPSHOT 11 | 12 | 13 | antlr-examples 14 | pom 15 | 16 | 17 | antlr-listener-example 18 | antlr-visitor-example1 19 | antlr-visitor.example2 20 | 21 | 22 | 23 | 24 | 25 | org.antlr 26 | antlr4-runtime 27 | ${antlr4.version} 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /parser-examples/javaparser-example/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | com.arjunsk 9 | parser-examples 10 | 1.0-SNAPSHOT 11 | 12 | 13 | javaparser-example 14 | 15 | 16 | 17 | 18 | com.github.javaparser 19 | javaparser-symbol-solver-core 20 | ${javaparser-symbol-solver-core.version} 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /parser-examples/javaparser-example/src/main/java/com/arjunsk/parser/javaparser/JavaparserDriver.java: -------------------------------------------------------------------------------- 1 | package com.arjunsk.parser.javaparser; 2 | 3 | import com.github.javaparser.StaticJavaParser; 4 | import com.github.javaparser.ast.CompilationUnit; 5 | import com.github.javaparser.ast.stmt.Statement; 6 | import java.io.BufferedReader; 7 | import java.io.InputStream; 8 | import java.io.InputStreamReader; 9 | import java.util.stream.Collectors; 10 | 11 | public class JavaparserDriver { 12 | 13 | public static void main(String[] args) { 14 | String code = getResourceFileAsString("input/SimpleProgram.java"); 15 | assert code != null; 16 | 17 | parse(code); 18 | } 19 | 20 | /** 21 | * Identify all Statements and see if it is an IfStatement. If yes, print the content of Statement 22 | * Object. 23 | */ 24 | private static void parse(String code) { 25 | CompilationUnit cu = StaticJavaParser.parse(code); 26 | 27 | cu.findAll(Statement.class) 28 | .forEach( 29 | statement -> { 30 | if (statement.isIfStmt()) { 31 | System.out.println("If statement content\n" + statement); 32 | } 33 | }); 34 | } 35 | 36 | /** Read String from File. */ 37 | public static String getResourceFileAsString(String fileName) { 38 | ClassLoader classLoader = ClassLoader.getSystemClassLoader(); 39 | try (InputStream is = classLoader.getResourceAsStream(fileName)) { 40 | if (is == null) return null; 41 | try (InputStreamReader isr = new InputStreamReader(is); 42 | BufferedReader reader = new BufferedReader(isr)) { 43 | return reader.lines().collect(Collectors.joining(System.lineSeparator())); 44 | } 45 | } catch (Exception ex) { 46 | throw new RuntimeException("Error reading file", ex); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /parser-examples/javaparser-example/src/main/resources/input/SimpleProgram.java: -------------------------------------------------------------------------------- 1 | public class SimpleProgram { 2 | 3 | public static void main(String[] args) { 4 | int a = 10; 5 | int b = 20; 6 | int c = a + b; 7 | System.out.println(c + ""); 8 | 9 | for (int age = 1; age < 20; age++) { 10 | System.out.println("Your age is " + age); 11 | 12 | if (age > 18) { 13 | System.out.println("You can vote in your current age"); 14 | } 15 | } 16 | 17 | for (int vote = 0; vote < 5; vote++) { 18 | System.out.println("You voted " + vote + " times."); 19 | } 20 | 21 | if (true) { 22 | System.out.println("True"); 23 | } else { 24 | System.out.println("False"); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /parser-examples/jdt-example/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | com.arjunsk 9 | parser-examples 10 | 1.0-SNAPSHOT 11 | 12 | 13 | jdt-example 14 | 15 | 16 | 17 | 18 | org.eclipse.jdt 19 | org.eclipse.jdt.core 20 | ${org.eclipse.jdt.core.version} 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /parser-examples/jdt-example/src/main/java/com/arjunsk/parser/jdt/JdtDriver.java: -------------------------------------------------------------------------------- 1 | package com.arjunsk.parser.jdt; 2 | 3 | import com.arjunsk.parser.jdt.ast.JdtAstVisitor; 4 | import java.io.BufferedReader; 5 | import java.io.InputStream; 6 | import java.io.InputStreamReader; 7 | import java.util.stream.Collectors; 8 | import org.eclipse.jdt.core.dom.AST; 9 | import org.eclipse.jdt.core.dom.ASTParser; 10 | import org.eclipse.jdt.core.dom.CompilationUnit; 11 | 12 | public class JdtDriver { 13 | 14 | public static void main(String[] args) { 15 | String javaCode = getResourceFileAsString("input/SimpleProgram.java"); 16 | assert javaCode != null; 17 | 18 | ASTParser parser = ASTParser.newParser(AST.JLS15); 19 | parser.setSource(javaCode.toCharArray()); // setting source code. 20 | parser.setKind(ASTParser.K_COMPILATION_UNIT); 21 | final CompilationUnit cu = (CompilationUnit) parser.createAST(null); // Root of tree 22 | 23 | cu.accept(new JdtAstVisitor(cu)); 24 | } 25 | 26 | /** Read File as String. */ 27 | public static String getResourceFileAsString(String fileName) { 28 | ClassLoader classLoader = ClassLoader.getSystemClassLoader(); 29 | try (InputStream is = classLoader.getResourceAsStream(fileName)) { 30 | if (is == null) return null; 31 | try (InputStreamReader isr = new InputStreamReader(is); 32 | BufferedReader reader = new BufferedReader(isr)) { 33 | return reader.lines().collect(Collectors.joining(System.lineSeparator())); 34 | } 35 | } catch (Exception ex) { 36 | throw new RuntimeException("Error reading file", ex); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /parser-examples/jdt-example/src/main/java/com/arjunsk/parser/jdt/ast/JdtAstVisitor.java: -------------------------------------------------------------------------------- 1 | package com.arjunsk.parser.jdt.ast; 2 | 3 | import java.util.HashSet; 4 | import java.util.Set; 5 | import org.eclipse.jdt.core.dom.ASTVisitor; 6 | import org.eclipse.jdt.core.dom.CompilationUnit; 7 | import org.eclipse.jdt.core.dom.ForStatement; 8 | import org.eclipse.jdt.core.dom.IfStatement; 9 | import org.eclipse.jdt.core.dom.SimpleName; 10 | import org.eclipse.jdt.core.dom.VariableDeclarationFragment; 11 | 12 | /** Visitor for all Java Grammar Semantics. */ 13 | public class JdtAstVisitor extends ASTVisitor { 14 | 15 | private final Set names; 16 | 17 | private final CompilationUnit cu; 18 | 19 | public JdtAstVisitor(CompilationUnit cu) { 20 | this.cu = cu; 21 | names = new HashSet<>(); 22 | } 23 | 24 | public boolean visit(VariableDeclarationFragment node) { 25 | SimpleName name = node.getName(); 26 | this.names.add(name.getIdentifier()); 27 | System.out.println( 28 | "Declared '" + name + "' at line " + cu.getLineNumber(name.getStartPosition())); 29 | return false; 30 | } 31 | 32 | public boolean visit(SimpleName node) { 33 | if (this.names.contains(node.getIdentifier())) { 34 | System.out.println( 35 | "Used '" + node + "' at line " + cu.getLineNumber(node.getStartPosition())); 36 | } 37 | return false; 38 | } 39 | 40 | public boolean visit(ForStatement node) { 41 | System.out.println("ForStatement -- content:\n" + node.toString()); 42 | return false; // if true, will iterate Visitor to it child node as well. 43 | } 44 | 45 | public boolean visit(IfStatement node) { 46 | System.out.println("IFStatement -- content:\n" + node.toString()); 47 | return false; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /parser-examples/jdt-example/src/main/resources/input/SimpleProgram.java: -------------------------------------------------------------------------------- 1 | public class SimpleProgram { 2 | 3 | public static void main(String[] args) { 4 | int a = 10; 5 | int b = 20; 6 | int c = a + b; 7 | System.out.println(c + ""); 8 | 9 | for (int age = 1; age < 20; age++) { 10 | System.out.println("Your age is " + age); 11 | 12 | if (age > 18) { 13 | System.out.println("You can vote in your current age"); 14 | } 15 | } 16 | 17 | for (int vote = 0; vote < 5; vote++) { 18 | System.out.println("You voted " + vote + " times."); 19 | } 20 | 21 | if (true) { 22 | System.out.println("True"); 23 | } else { 24 | System.out.println("False"); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /parser-examples/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | com.arjunsk 9 | codekrypt-compiler 10 | 1.0-SNAPSHOT 11 | 12 | 13 | parser-examples 14 | pom 15 | 16 | 17 | javaparser-example 18 | jdt-example 19 | antlr-examples 20 | 21 | 22 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.arjunsk 8 | codekrypt-compiler 9 | 1.0-SNAPSHOT 10 | pom 11 | 12 | 13 | compiler-examples 14 | parser-examples 15 | 16 | 17 | 18 | 1.8 19 | ${java.version} 20 | ${java.version} 21 | UTF-8 22 | UTF-8 23 | 24 | 4.13.2 25 | 26 | 3.20.0 27 | 3.24.0 28 | 4.9.2 29 | 6.0 30 | 31 | 3.8.1 32 | 33 | 34 | 35 | 36 | 37 | junit 38 | junit 39 | ${junit.version} 40 | test 41 | 42 | 43 | 44 | 45 | --------------------------------------------------------------------------------