From 7d5ec6b9f3d5805e3e668f5dad8ef01521378b6f Mon Sep 17 00:00:00 2001 From: Peter Jakubco Date: Thu, 12 May 2022 10:53:26 +0200 Subject: [PATCH] [#201] RASP reimplementation --- application/build.gradle | 20 +- .../ramc-ram/src/main/antlr/RAMParser.g4 | 4 +- .../plugins/compiler/ram/CompilerRAM.java | 18 +- .../ram/{visitors => }/ProgramParser.java | 4 +- .../plugins/compiler/ram/Runner.java | 2 +- .../raspc-rasp/src/main/antlr/RASPLexer.g4 | 58 +++ .../raspc-rasp/src/main/antlr/RASPParser.g4 | 58 +++ .../raspc-rasp/src/main/cup/parser.cup | 159 ------- .../plugins/compiler/rasp/CompilerRASP.java | 177 ++++++++ .../compiler/rasp/LexicalAnalyzerImpl.java | 95 ++++ .../compiler/rasp/ParserErrorListener.java | 25 ++ .../plugins/compiler/rasp/ParsingUtils.java | 50 +++ .../plugins/compiler/rasp/ProgramParser.java | 151 +++++++ .../compiler/{raspc => rasp}/Runner.java | 8 +- .../compiler/rasp/ast/Instruction.java | 62 +++ .../plugins/compiler/rasp/ast/Label.java | 54 +++ .../plugins/compiler/rasp/ast/Program.java | 126 ++++++ .../rasp/exceptions/CompileException.java | 20 + .../rasp/exceptions/SyntaxErrorException.java | 11 + .../plugins/compiler/raspc/CompilerImpl.java | 205 --------- .../compiler/raspc/CompilerOutput.java | 116 ----- .../plugins/compiler/raspc/Namespace.java | 28 -- .../plugins/compiler/raspc/Statement.java | 57 --- .../plugins/compiler/raspc/TokenImpl.java | 88 ---- .../compiler/raspc/tree/AbstractTreeNode.java | 24 - .../plugins/compiler/raspc/tree/Input.java | 42 -- .../plugins/compiler/raspc/tree/Label.java | 49 --- .../plugins/compiler/raspc/tree/Program.java | 68 --- .../plugins/compiler/raspc/tree/Row.java | 63 --- .../compiler/raspc/tree/SourceCode.java | 69 --- .../raspc-rasp/src/main/jflex/lexer.jflex | 184 -------- .../{raspc => rasp}/version.properties | 0 .../{raspc => rasp}/AbstractCompilerTest.java | 10 +- .../{raspc => rasp}/CompilerTest.java | 26 +- .../plugins/compiler/rasp/MemoryStub.java | 74 ++++ .../plugins/compiler/raspc/MemoryStub.java | 84 ---- .../emustudio/plugins/cpu/ram/CpuImpl.java | 6 +- .../plugins/cpu/ram/EmulatorEngine.java | 15 +- .../plugins/cpu/ram/api/RAMCpuContext.java | 31 ++ .../plugins/cpu/ram/EmulatorEngineTest.java | 44 +- plugins/cpu/rasp-cpu/build.gradle | 1 - .../emustudio/plugins/cpu/rasp/CpuImpl.java | 47 +- .../plugins/cpu/rasp/EmulatorEngine.java | 415 +++++++----------- .../plugins/cpu/rasp/RASPCpuContext.java | 116 ----- .../plugins/cpu/rasp/RASPCpuContextImpl.java | 48 ++ .../plugins/cpu/rasp/api/RASPCpuContext.java} | 41 +- .../cpu/rasp/gui/LabelDebugColumn.java | 3 +- .../cpu/rasp/gui/RASPCpuStatusPanel.java | 13 +- .../cpu/rasp/gui/RASPDisassembler.java | 66 +-- .../plugins/cpu/rasp/EmulatorEngineTest.java | 54 +-- .../plugins/cpu/rasp/MemoryStub.java | 74 ++++ .../emustudio/plugins/cpu/rasp/RASPCell.java | 38 ++ .../plugins/cpu/rasp/RaspMemoryStub.java | 93 ---- .../abstracttape/AbstractTapeContextImpl.java | 53 ++- .../abstracttape/api/AbstractTapeContext.java | 31 +- .../device/abstracttape/gui/TapeGui.java | 16 +- .../plugins/memory/rasp/InstructionImpl.java | 104 ----- .../memory/rasp/MemoryContextImpl.java | 223 +++++----- .../plugins/memory/rasp/MemoryImpl.java | 5 + .../plugins/memory/rasp/api/MemoryItem.java | 30 -- .../memory/rasp/api/RASPInstruction.java | 115 ----- .../plugins/memory/rasp/api/RASPLabel.java | 23 + .../memory/rasp/api/RASPMemoryCell.java | 12 + .../memory/rasp/api/RASPMemoryContext.java | 42 +- .../plugins/memory/rasp/gui/Disassembler.java | 38 ++ .../plugins/memory/rasp/gui/MemoryDialog.java | 2 +- .../memory/rasp/gui/RASPTableModel.java | 119 +++-- settings.gradle | 6 +- 68 files changed, 1812 insertions(+), 2401 deletions(-) rename plugins/compiler/ramc-ram/src/main/java/net/emustudio/plugins/compiler/ram/{visitors => }/ProgramParser.java (96%) create mode 100644 plugins/compiler/raspc-rasp/src/main/antlr/RASPLexer.g4 create mode 100644 plugins/compiler/raspc-rasp/src/main/antlr/RASPParser.g4 delete mode 100644 plugins/compiler/raspc-rasp/src/main/cup/parser.cup create mode 100644 plugins/compiler/raspc-rasp/src/main/java/net/emustudio/plugins/compiler/rasp/CompilerRASP.java create mode 100644 plugins/compiler/raspc-rasp/src/main/java/net/emustudio/plugins/compiler/rasp/LexicalAnalyzerImpl.java create mode 100644 plugins/compiler/raspc-rasp/src/main/java/net/emustudio/plugins/compiler/rasp/ParserErrorListener.java create mode 100644 plugins/compiler/raspc-rasp/src/main/java/net/emustudio/plugins/compiler/rasp/ParsingUtils.java create mode 100644 plugins/compiler/raspc-rasp/src/main/java/net/emustudio/plugins/compiler/rasp/ProgramParser.java rename plugins/compiler/raspc-rasp/src/main/java/net/emustudio/plugins/compiler/{raspc => rasp}/Runner.java (93%) create mode 100644 plugins/compiler/raspc-rasp/src/main/java/net/emustudio/plugins/compiler/rasp/ast/Instruction.java create mode 100644 plugins/compiler/raspc-rasp/src/main/java/net/emustudio/plugins/compiler/rasp/ast/Label.java create mode 100644 plugins/compiler/raspc-rasp/src/main/java/net/emustudio/plugins/compiler/rasp/ast/Program.java create mode 100644 plugins/compiler/raspc-rasp/src/main/java/net/emustudio/plugins/compiler/rasp/exceptions/CompileException.java create mode 100644 plugins/compiler/raspc-rasp/src/main/java/net/emustudio/plugins/compiler/rasp/exceptions/SyntaxErrorException.java delete mode 100644 plugins/compiler/raspc-rasp/src/main/java/net/emustudio/plugins/compiler/raspc/CompilerImpl.java delete mode 100644 plugins/compiler/raspc-rasp/src/main/java/net/emustudio/plugins/compiler/raspc/CompilerOutput.java delete mode 100644 plugins/compiler/raspc-rasp/src/main/java/net/emustudio/plugins/compiler/raspc/Namespace.java delete mode 100644 plugins/compiler/raspc-rasp/src/main/java/net/emustudio/plugins/compiler/raspc/Statement.java delete mode 100644 plugins/compiler/raspc-rasp/src/main/java/net/emustudio/plugins/compiler/raspc/TokenImpl.java delete mode 100644 plugins/compiler/raspc-rasp/src/main/java/net/emustudio/plugins/compiler/raspc/tree/AbstractTreeNode.java delete mode 100644 plugins/compiler/raspc-rasp/src/main/java/net/emustudio/plugins/compiler/raspc/tree/Input.java delete mode 100644 plugins/compiler/raspc-rasp/src/main/java/net/emustudio/plugins/compiler/raspc/tree/Label.java delete mode 100644 plugins/compiler/raspc-rasp/src/main/java/net/emustudio/plugins/compiler/raspc/tree/Program.java delete mode 100644 plugins/compiler/raspc-rasp/src/main/java/net/emustudio/plugins/compiler/raspc/tree/Row.java delete mode 100644 plugins/compiler/raspc-rasp/src/main/java/net/emustudio/plugins/compiler/raspc/tree/SourceCode.java delete mode 100644 plugins/compiler/raspc-rasp/src/main/jflex/lexer.jflex rename plugins/compiler/raspc-rasp/src/main/resources/net/emustudio/plugins/compiler/{raspc => rasp}/version.properties (100%) rename plugins/compiler/raspc-rasp/src/test/java/net/emustudio/plugins/compiler/{raspc => rasp}/AbstractCompilerTest.java (90%) rename plugins/compiler/raspc-rasp/src/test/java/net/emustudio/plugins/compiler/{raspc => rasp}/CompilerTest.java (51%) create mode 100644 plugins/compiler/raspc-rasp/src/test/java/net/emustudio/plugins/compiler/rasp/MemoryStub.java delete mode 100644 plugins/compiler/raspc-rasp/src/test/java/net/emustudio/plugins/compiler/raspc/MemoryStub.java delete mode 100644 plugins/cpu/rasp-cpu/src/main/java/net/emustudio/plugins/cpu/rasp/RASPCpuContext.java create mode 100644 plugins/cpu/rasp-cpu/src/main/java/net/emustudio/plugins/cpu/rasp/RASPCpuContextImpl.java rename plugins/{memory/rasp-mem/src/main/java/net/emustudio/plugins/memory/rasp/NumberMemoryItem.java => cpu/rasp-cpu/src/main/java/net/emustudio/plugins/cpu/rasp/api/RASPCpuContext.java} (51%) create mode 100644 plugins/cpu/rasp-cpu/src/test/java/net/emustudio/plugins/cpu/rasp/MemoryStub.java create mode 100644 plugins/cpu/rasp-cpu/src/test/java/net/emustudio/plugins/cpu/rasp/RASPCell.java delete mode 100644 plugins/cpu/rasp-cpu/src/test/java/net/emustudio/plugins/cpu/rasp/RaspMemoryStub.java delete mode 100644 plugins/memory/rasp-mem/src/main/java/net/emustudio/plugins/memory/rasp/InstructionImpl.java delete mode 100644 plugins/memory/rasp-mem/src/main/java/net/emustudio/plugins/memory/rasp/api/MemoryItem.java delete mode 100644 plugins/memory/rasp-mem/src/main/java/net/emustudio/plugins/memory/rasp/api/RASPInstruction.java create mode 100644 plugins/memory/rasp-mem/src/main/java/net/emustudio/plugins/memory/rasp/api/RASPLabel.java create mode 100644 plugins/memory/rasp-mem/src/main/java/net/emustudio/plugins/memory/rasp/api/RASPMemoryCell.java create mode 100644 plugins/memory/rasp-mem/src/main/java/net/emustudio/plugins/memory/rasp/gui/Disassembler.java diff --git a/application/build.gradle b/application/build.gradle index e1192e018..77888b364 100644 --- a/application/build.gradle +++ b/application/build.gradle @@ -1,7 +1,7 @@ /* * This file is part of emuStudio. * - * Copyright (C) 2006-2020 Peter Jakubčo + * Copyright (C) 2006-2022 Peter Jakubčo * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -54,17 +54,17 @@ dependencies { providedRuntime project(":plugins:compiler:as-z80") providedRuntime project(":plugins:compiler:brainc-brainduck") providedRuntime project(":plugins:compiler:ramc-ram") -// providedRuntime project(":plugins:compiler:raspc-rasp") + providedRuntime project(":plugins:compiler:raspc-rasp") providedRuntime project(":plugins:memory:ram-mem") - // providedRuntime project(":plugins:memory:rasp-mem") + providedRuntime project(":plugins:memory:rasp-mem") providedRuntime project(":plugins:memory:ssem-mem") providedRuntime project(":plugins:memory:byte-mem") providedRuntime project(":plugins:cpu:8080-cpu") providedRuntime project(":plugins:cpu:brainduck-cpu") providedRuntime project(":plugins:cpu:ram-cpu") - // providedRuntime project(":plugins:cpu:rasp-cpu") + providedRuntime project(":plugins:cpu:rasp-cpu") providedRuntime project(":plugins:cpu:z80-cpu") providedRuntime project(":plugins:cpu:ssem-cpu") @@ -180,13 +180,13 @@ distributions { from(output(":plugins:compiler:as-ssem")) from(output(":plugins:compiler:brainc-brainduck")) from(output(":plugins:compiler:ramc-ram")) - //from(output(":plugins:compiler:raspc-rasp")) + from(output(":plugins:compiler:raspc-rasp")) } into('memory') { include '*.jar' from(output(":plugins:memory:ram-mem")) - // from(output(":plugins:memory:rasp-mem")) + from(output(":plugins:memory:rasp-mem")) from(output(":plugins:memory:ssem-mem")) from(output(":plugins:memory:byte-mem")) } @@ -196,7 +196,7 @@ distributions { from(output(":plugins:cpu:8080-cpu")) from(output(":plugins:cpu:brainduck-cpu")) from(output(":plugins:cpu:ram-cpu")) - // from(output(":plugins:cpu:rasp-cpu")) + from(output(":plugins:cpu:rasp-cpu")) from(output(":plugins:cpu:z80-cpu")) from(output(":plugins:cpu:ssem-cpu")) } @@ -213,16 +213,14 @@ distributions { } // Examples - //["as-8080", "as-z80", "as-ssem", "brainc-brainduck", "ramc-ram", "raspc-rasp"] - ["as-8080", "as-z80", "as-ssem", "brainc-brainduck", "ramc-ram"].collect { compiler -> + ["as-8080", "as-z80", "as-ssem", "brainc-brainduck", "ramc-ram", "raspc-rasp"].collect { compiler -> from(examples(":plugins:compiler:$compiler")) { into "examples/$compiler" } } // Scripts - // ["as-8080", "as-z80", "as-ssem", "brainc-brainduck", "ramc-ram", "raspc-rasp"] - ["as-8080", "as-z80", "as-ssem", "brainc-brainduck", "ramc-ram"].collect { compiler -> + ["as-8080", "as-z80", "as-ssem", "brainc-brainduck", "ramc-ram", "raspc-rasp"].collect { compiler -> from(scripts(":plugins:compiler:$compiler")) { into "bin" } diff --git a/plugins/compiler/ramc-ram/src/main/antlr/RAMParser.g4 b/plugins/compiler/ramc-ram/src/main/antlr/RAMParser.g4 index 3ba07ba1a..74d512290 100644 --- a/plugins/compiler/ramc-ram/src/main/antlr/RAMParser.g4 +++ b/plugins/compiler/ramc-ram/src/main/antlr/RAMParser.g4 @@ -18,8 +18,8 @@ rLine: comment: COMMENT? | COMMENT2?; rStatement: - instr=rInstruction - | value=rInput + rInstruction + | rInput ; rInstruction: diff --git a/plugins/compiler/ramc-ram/src/main/java/net/emustudio/plugins/compiler/ram/CompilerRAM.java b/plugins/compiler/ramc-ram/src/main/java/net/emustudio/plugins/compiler/ram/CompilerRAM.java index 8687ea03b..81fe1470d 100644 --- a/plugins/compiler/ramc-ram/src/main/java/net/emustudio/plugins/compiler/ram/CompilerRAM.java +++ b/plugins/compiler/ramc-ram/src/main/java/net/emustudio/plugins/compiler/ram/CompilerRAM.java @@ -24,9 +24,7 @@ import net.emustudio.emulib.plugins.compiler.LexicalAnalyzer; import net.emustudio.emulib.plugins.compiler.SourceFileExtension; import net.emustudio.emulib.runtime.*; -import net.emustudio.emulib.runtime.helpers.RadixUtils; import net.emustudio.plugins.compiler.ram.ast.Program; -import net.emustudio.plugins.compiler.ram.visitors.ProgramParser; import net.emustudio.plugins.memory.ram.api.RAMMemoryContext; import org.antlr.v4.runtime.CharStream; import org.antlr.v4.runtime.CharStreams; @@ -39,7 +37,7 @@ import java.io.Reader; import java.util.*; -@PluginRoot(type = PLUGIN_TYPE.COMPILER, title = "RAM Compiler") +@PluginRoot(type = PLUGIN_TYPE.COMPILER, title = "RAM Machine Assembler") @SuppressWarnings("unused") public class CompilerRAM extends AbstractCompiler { private final static Logger LOGGER = LoggerFactory.getLogger(CompilerRAM.class); @@ -63,15 +61,17 @@ public String getCopyright() { @Override public String getDescription() { - return "RAM machine compiler"; + return "RAM machine assembler"; } public void initialize() { - try { - this.memory = applicationApi.getContextPool().getMemoryContext(pluginID, RAMMemoryContext.class); - } catch (InvalidContextException | ContextNotFoundException e) { - LOGGER.warn("Memory is not available", e); - } + Optional.ofNullable(applicationApi.getContextPool()).ifPresent(pool -> { + try { + memory = pool.getMemoryContext(pluginID, RAMMemoryContext.class); + } catch (InvalidContextException | ContextNotFoundException e) { + LOGGER.warn("Memory is not available", e); + } + }); } @Override diff --git a/plugins/compiler/ramc-ram/src/main/java/net/emustudio/plugins/compiler/ram/visitors/ProgramParser.java b/plugins/compiler/ramc-ram/src/main/java/net/emustudio/plugins/compiler/ram/ProgramParser.java similarity index 96% rename from plugins/compiler/ramc-ram/src/main/java/net/emustudio/plugins/compiler/ram/visitors/ProgramParser.java rename to plugins/compiler/ramc-ram/src/main/java/net/emustudio/plugins/compiler/ram/ProgramParser.java index 74bef6ff4..db9367f52 100644 --- a/plugins/compiler/ramc-ram/src/main/java/net/emustudio/plugins/compiler/ram/visitors/ProgramParser.java +++ b/plugins/compiler/ramc-ram/src/main/java/net/emustudio/plugins/compiler/ram/ProgramParser.java @@ -1,7 +1,5 @@ -package net.emustudio.plugins.compiler.ram.visitors; +package net.emustudio.plugins.compiler.ram; -import net.emustudio.plugins.compiler.ram.RAMParser; -import net.emustudio.plugins.compiler.ram.RAMParserBaseVisitor; import net.emustudio.plugins.compiler.ram.ast.Instruction; import net.emustudio.plugins.compiler.ram.ast.Label; import net.emustudio.plugins.compiler.ram.ast.Program; diff --git a/plugins/compiler/ramc-ram/src/main/java/net/emustudio/plugins/compiler/ram/Runner.java b/plugins/compiler/ramc-ram/src/main/java/net/emustudio/plugins/compiler/ram/Runner.java index dd14fd8e8..90aae1f80 100644 --- a/plugins/compiler/ramc-ram/src/main/java/net/emustudio/plugins/compiler/ram/Runner.java +++ b/plugins/compiler/ramc-ram/src/main/java/net/emustudio/plugins/compiler/ram/Runner.java @@ -57,7 +57,7 @@ public static void main(String... args) { } else { outputFile = inputFile; } - outputFile += ".hex"; + outputFile += ".bram"; } CompilerRAM compiler = new CompilerRAM(0L, ApplicationApi.UNAVAILABLE, PluginSettings.UNAVAILABLE); diff --git a/plugins/compiler/raspc-rasp/src/main/antlr/RASPLexer.g4 b/plugins/compiler/raspc-rasp/src/main/antlr/RASPLexer.g4 new file mode 100644 index 000000000..b18440e9d --- /dev/null +++ b/plugins/compiler/raspc-rasp/src/main/antlr/RASPLexer.g4 @@ -0,0 +1,58 @@ +lexer grammar RASPLexer; + +COMMENT: ('//' | '--' | ';' | '#' ) ~[\r\n]*; +COMMENT2: '/*' .*? '*/'; + +fragment A: [aA]; +fragment B: [bB]; +fragment D: [dD]; +fragment E: [eE]; +fragment G: [gG]; +fragment H: [hH]; +fragment I: [iI]; +fragment J: [jJ]; +fragment L: [lL]; +fragment M: [mM]; +fragment N: [nN]; +fragment O: [oO]; +fragment P: [pP]; +fragment R: [rR]; +fragment S: [sS]; +fragment T: [tT]; +fragment U: [uU]; +fragment V: [vV]; +fragment W: [wW]; +fragment X: [xX]; +fragment Z: [zZ]; + +OPCODE_READ: R E A D; +OPCODE_WRITE: W R I T E; +OPCODE_LOAD: L O A D; +OPCODE_STORE: S T O R E; +OPCODE_ADD: A D D; +OPCODE_SUB: S U B; +OPCODE_MUL: M U L; +OPCODE_DIV: D I V; +OPCODE_JMP: J M P; +OPCODE_JZ: J Z; +OPCODE_JGTZ: J G T Z; +OPCODE_HALT: H A L T; + +OP_CONSTANT: '='; + +PREP_ORG: O R G; +PREP_INPUT: '<' I N P U T '>'; + +LIT_HEXNUMBER_1: '0' X [0-9a-fA-F]+; +LIT_NUMBER: [0-9]+ D?; +LIT_HEXNUMBER_2: [0-9a-fA-F]+ H; +LIT_OCTNUMBER: [0-7]+ [oOqQ]; +LIT_BINNUMBER: [01]+ B; + +ID_IDENTIFIER: [a-zA-Z_?@] [a-zA-Z_?@0-9]*; +ID_LABEL: ID_IDENTIFIER ':'; + +WS : [ \t\f]+ -> channel(HIDDEN); +EOL: '\r'? '\n'; + +ERROR: .; diff --git a/plugins/compiler/raspc-rasp/src/main/antlr/RASPParser.g4 b/plugins/compiler/raspc-rasp/src/main/antlr/RASPParser.g4 new file mode 100644 index 000000000..fc3f7e42a --- /dev/null +++ b/plugins/compiler/raspc-rasp/src/main/antlr/RASPParser.g4 @@ -0,0 +1,58 @@ +parser grammar RASPParser; + +options { + tokenVocab = RASPLexer; +} + +rStart: + (rLine EOL rLine)* EOF + | rLine EOF + ; + +rLine: + label=ID_LABEL? statement=rStatement comment + | label=ID_LABEL comment + | comment + ; + +comment: COMMENT? | COMMENT2?; + +rStatement: + rInstruction + | rInput + | rOrg + ; + + +rInstruction: + op=OPCODE_READ n=rNumber # instrRegister + | op=OPCODE_WRITE OP_CONSTANT n=rNumber # instrConstant + | op=OPCODE_WRITE n=rNumber # instrRegister + | op=OPCODE_LOAD OP_CONSTANT n=rNumber # instrConstant + | op=OPCODE_LOAD n=rNumber # instrRegister + | op=OPCODE_STORE n=rNumber # instrRegister + | op=OPCODE_ADD OP_CONSTANT n=rNumber # instrConstant + | op=OPCODE_ADD n=rNumber # instrRegister + | op=OPCODE_SUB OP_CONSTANT n=rNumber # instrConstant + | op=OPCODE_SUB n=rNumber # instrRegister + | op=OPCODE_MUL OP_CONSTANT n=rNumber # instrConstant + | op=OPCODE_MUL n=rNumber # instrRegister + | op=OPCODE_DIV OP_CONSTANT n=rNumber # instrConstant + | op=OPCODE_DIV n=rNumber # instrRegister + | op=OPCODE_JMP id=ID_IDENTIFIER # instrJump + | op=OPCODE_JZ id=ID_IDENTIFIER # instrJump + | op=OPCODE_JGTZ id=ID_IDENTIFIER # instrJump + | op=OPCODE_HALT # instrNoOperand + ; + +rInput: PREP_INPUT rNumber+; + +rOrg: PREP_ORG n=rNumber; + +rNumber: + n=LIT_HEXNUMBER_1 + | n=LIT_NUMBER + | n=LIT_HEXNUMBER_2 + | n=LIT_OCTNUMBER + | n=LIT_BINNUMBER + ; diff --git a/plugins/compiler/raspc-rasp/src/main/cup/parser.cup b/plugins/compiler/raspc-rasp/src/main/cup/parser.cup deleted file mode 100644 index 51c5a07d9..000000000 --- a/plugins/compiler/raspc-rasp/src/main/cup/parser.cup +++ /dev/null @@ -1,159 +0,0 @@ -/* - * This file is part of emuStudio. - * - * Copyright (C) 2016-2017 Michal Šipoš - * Copyright (C) 2020 Peter Jakubčo - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package net.emustudio.plugins.compiler.raspc; - -import java_cup.runtime.DefaultSymbolFactory; -import net.emustudio.emulib.plugins.compiler.CompilerMessage; -import net.emustudio.emulib.plugins.compiler.Token; -import net.emustudio.plugins.compiler.raspc.tree.*; -import net.emustudio.plugins.memory.rasp.InstructionImpl; -import net.emustudio.plugins.memory.rasp.api.RASPInstruction; - -import java.util.List; -import java.util.Objects; -import java.util.stream.Collectors; - -parser code{: - private CompilerImpl compiler; - private boolean syntaxErrors; - - public ParserImpl(LexerImpl lexer, CompilerImpl compiler) { - super(lexer, new DefaultSymbolFactory()); - this.compiler = Objects.requireNonNull(compiler); - } - - public void reset() { - syntaxErrors = false; - } - - @Override - public void report_fatal_error(String message, Object info) throws Exception { - done_parsing(); - report_error(message, info); - throw new Exception("Can't recover from previous error(s)"); - } - - @Override - public void report_error(String messageText, Object current) { - syntaxErrors = true; - - Token token = (Token)current; - - messageText += ":" + token.getErrorString() + " ('" + token.getText() + "')"; - - List expectedTokenIds = expected_token_ids() - .stream() - .map(this::symbl_name_from_id) - .collect(Collectors.toList()); - - if (!expectedTokenIds.isEmpty()) { - messageText += "\nExpected tokens: " + expectedTokenIds; - } - - CompilerMessage message = new CompilerMessage( - CompilerMessage.MessageType.TYPE_ERROR, messageText, token.getLine()+1, token.getColumn() - ); - - compiler.notifyOnMessage(message); - } - - public boolean hasSyntaxErrors() { - return syntaxErrors; - } -:}; - -terminal READ, WRITE, LOAD, STORE, ADD, SUB, MUL, DIV, JMP, JZ, JGTZ, HALT, ORG, SEPARATOR_EOL, TCOMMENT, OPERATOR_CONSTANT; -terminal Integer NUMBER; -terminal String TLABEL, IDENT; -terminal TINPUT; - -non terminal SourceCode SourceCode; -non terminal Program Program; -non terminal Row Row; -non terminal Label Label; -non terminal Comment; -non terminal Statement Statement; -non terminal InstructionImpl Instruction; -non terminal InstructionImpl JumpInstruction; -non terminal Input Input; - - -start with SourceCode; - -SourceCode ::= Program:p {: RESULT = new SourceCode(p); :} - ; - -Program ::= Row:row - {: - Program program = new Program(); - if (row != null) program.addRow(row); - RESULT = program; - :} - | Program:program SEPARATOR_EOL Row:row - {: - if (row != null) program.addRow(row); - RESULT = program; - :} - ; - - -Row ::= Label:l Statement:s Comment {: RESULT = new Row( l, null, -1, s); :} - | Label:l Comment {: RESULT = new Row( l, null, -1, null); :} - | ORG NUMBER:n Comment {: RESULT = new Row(null, null, n, null); :} - | TINPUT Input:i Comment {: RESULT = new Row(null, i, -1, null); :} - ; - - -Label ::= TLABEL:l {: RESULT = new Label(l); :} - | {: RESULT = null; :} - ; - -Statement ::= Instruction:i NUMBER:operand {: RESULT = new Statement(i,operand); :} - | JumpInstruction:i IDENT:label {: RESULT = new Statement(i,label); :} - | HALT {: RESULT = new Statement(new InstructionImpl(RASPInstruction.HALT), 0); :} - ; - -Input ::= Input:i NUMBER:n {: i.addNumber(n); RESULT = i; :} - | NUMBER:n {: RESULT = new Input(n); :} - ; - -Comment ::= TCOMMENT | /*no comment*/; /*no action - ignore the comment*/ - -Instruction ::= - READ {: RESULT = new InstructionImpl(RASPInstruction.READ); :} - | WRITE OPERATOR_CONSTANT {: RESULT = new InstructionImpl(RASPInstruction.WRITE_CONSTANT); :} - | WRITE {: RESULT = new InstructionImpl(RASPInstruction.WRITE_REGISTER); :} - | LOAD OPERATOR_CONSTANT {: RESULT = new InstructionImpl(RASPInstruction.LOAD_CONSTANT); :} - | LOAD {: RESULT = new InstructionImpl(RASPInstruction.LOAD_REGISTER); :} - | STORE {: RESULT = new InstructionImpl(RASPInstruction.STORE); :} - | ADD OPERATOR_CONSTANT {: RESULT = new InstructionImpl(RASPInstruction.ADD_CONSTANT); :} - | ADD {: RESULT = new InstructionImpl(RASPInstruction.ADD_REGISTER); :} - | SUB OPERATOR_CONSTANT {: RESULT = new InstructionImpl(RASPInstruction.SUB_CONSTANT); :} - | SUB {: RESULT = new InstructionImpl(RASPInstruction.SUB_REGISTER); :} - | MUL OPERATOR_CONSTANT {: RESULT = new InstructionImpl(RASPInstruction.MUL_CONSTANT); :} - | MUL {: RESULT = new InstructionImpl(RASPInstruction.MUL_REGISTER); :} - | DIV OPERATOR_CONSTANT {: RESULT = new InstructionImpl(RASPInstruction.DIV_CONSTANT); :} - | DIV {: RESULT = new InstructionImpl(RASPInstruction.DIV_REGISTER); :} - ; - -JumpInstruction ::= JMP {: RESULT = new InstructionImpl(RASPInstruction.JMP); :} - | JZ {: RESULT = new InstructionImpl(RASPInstruction.JZ); :} - | JGTZ {: RESULT = new InstructionImpl(RASPInstruction.JGTZ); :} - ; diff --git a/plugins/compiler/raspc-rasp/src/main/java/net/emustudio/plugins/compiler/rasp/CompilerRASP.java b/plugins/compiler/raspc-rasp/src/main/java/net/emustudio/plugins/compiler/rasp/CompilerRASP.java new file mode 100644 index 000000000..ccdf1d5a2 --- /dev/null +++ b/plugins/compiler/raspc-rasp/src/main/java/net/emustudio/plugins/compiler/rasp/CompilerRASP.java @@ -0,0 +1,177 @@ +/* + * This file is part of emuStudio. + * + * Copyright (C) 2016-2017 Michal Šipoš + * Copyright (C) 2020 Peter Jakubčo + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package net.emustudio.plugins.compiler.rasp; + +import net.emustudio.emulib.plugins.annotations.PLUGIN_TYPE; +import net.emustudio.emulib.plugins.annotations.PluginRoot; +import net.emustudio.emulib.plugins.compiler.AbstractCompiler; +import net.emustudio.emulib.plugins.compiler.LexicalAnalyzer; +import net.emustudio.emulib.plugins.compiler.SourceFileExtension; +import net.emustudio.emulib.runtime.ApplicationApi; +import net.emustudio.emulib.runtime.ContextNotFoundException; +import net.emustudio.emulib.runtime.InvalidContextException; +import net.emustudio.emulib.runtime.PluginSettings; +import net.emustudio.plugins.compiler.rasp.ast.Program; +import net.emustudio.plugins.memory.rasp.api.RASPMemoryCell; +import net.emustudio.plugins.memory.rasp.api.RASPMemoryContext; +import org.antlr.v4.runtime.CharStream; +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.CommonTokenStream; +import org.antlr.v4.runtime.TokenStream; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.FileReader; +import java.io.Reader; +import java.util.*; + +@PluginRoot( + type = PLUGIN_TYPE.COMPILER, + title = "RASP Machine Assembler" +) +public class CompilerRASP extends AbstractCompiler { + private final static Logger LOGGER = LoggerFactory.getLogger(CompilerRASP.class); + private static final List SOURCE_FILE_EXTENSIONS = List.of( + new SourceFileExtension("rasp", "RASP source file") + ); + + private RASPMemoryContext memory; + private int programLocation; + + public CompilerRASP(long pluginID, ApplicationApi applicationApi, PluginSettings settings) { + super(pluginID, applicationApi, settings); + } + + @Override + public void initialize() { + Optional.ofNullable(applicationApi.getContextPool()).ifPresent(pool -> { + try { + memory = pool.getMemoryContext(pluginID, RASPMemoryContext.class); + } catch (InvalidContextException | ContextNotFoundException e) { + LOGGER.warn("Memory is not available", e); + } + }); + } + + @Override + public boolean compile(String inputFileName, String outputFileName) { + try { + this.notifyCompileStart(); + notifyInfo(getTitle() + ", version " + getVersion()); + + try (Reader reader = new FileReader(inputFileName)) { + org.antlr.v4.runtime.Lexer lexer = createLexer(CharStreams.fromReader(reader)); + lexer.addErrorListener(new ParserErrorListener()); + CommonTokenStream tokens = new CommonTokenStream(lexer); + + RASPParser parser = createParser(tokens); + parser.addErrorListener(new ParserErrorListener()); + + Program program = new Program(); + new ProgramParser(program).visit(parser.rStart()); + + Map compiled = program.compile(); + this.programLocation = program.getProgramLocation(compiled); + program.saveToFile(outputFileName, compiled); + + notifyInfo(String.format("Compile was successful.\n\tOutput: %s", outputFileName)); + + if (memory != null) { + memory.clear(); + program.loadIntoMemory(memory, compiled); + notifyInfo("Compiled file was loaded into program memory."); + } else { + notifyWarning("Memory is not available"); + } + } + + } catch (Exception e) { + LOGGER.trace("Compilation failed", e); + notifyError("Compilation failed: " + e.getMessage()); + return false; + } finally { + notifyCompileFinish(); + } + return true; + } + + @Override + public boolean compile(String inputFileName) { + int i = inputFileName.toLowerCase(Locale.ENGLISH).lastIndexOf(".rasp"); + + String outputFileName = inputFileName; + if (i >= 0) { + outputFileName = outputFileName.substring(0, i); + } + outputFileName += ".brasp"; + return compile(inputFileName, outputFileName); + } + + @Override + public LexicalAnalyzer createLexer(String s) { + RASPLexer lexer = createLexer(CharStreams.fromString(s)); + return new LexicalAnalyzerImpl(lexer); + } + + @Override + public int getProgramLocation() { + return programLocation; + } + + @Override + public List getSourceFileExtensions() { + return SOURCE_FILE_EXTENSIONS; + } + + @Override + public String getVersion() { + return getResourceBundle().map(b -> b.getString("version")).orElse("(unknown)"); + } + + @Override + public String getCopyright() { + return getResourceBundle().map(b -> b.getString("copyright")).orElse("(unknown)"); + } + + @Override + public String getDescription() { + return "RASP machine assembler"; + } + + private Optional getResourceBundle() { + try { + return Optional.of(ResourceBundle.getBundle("net.emustudio.plugins.compiler.rasp.version")); + } catch (MissingResourceException e) { + return Optional.empty(); + } + } + + private RASPLexer createLexer(CharStream input) { + RASPLexer lexer = new RASPLexer(input); + lexer.removeErrorListeners(); + return lexer; + } + + private RASPParser createParser(TokenStream tokenStream) { + RASPParser parser = new RASPParser(tokenStream); + parser.removeErrorListeners(); + return parser; + } +} diff --git a/plugins/compiler/raspc-rasp/src/main/java/net/emustudio/plugins/compiler/rasp/LexicalAnalyzerImpl.java b/plugins/compiler/raspc-rasp/src/main/java/net/emustudio/plugins/compiler/rasp/LexicalAnalyzerImpl.java new file mode 100644 index 000000000..b392b87f4 --- /dev/null +++ b/plugins/compiler/raspc-rasp/src/main/java/net/emustudio/plugins/compiler/rasp/LexicalAnalyzerImpl.java @@ -0,0 +1,95 @@ +package net.emustudio.plugins.compiler.rasp; + +import net.emustudio.emulib.plugins.compiler.LexicalAnalyzer; +import net.emustudio.emulib.plugins.compiler.Token; +import org.antlr.v4.runtime.CharStreams; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; + +import static net.emustudio.plugins.compiler.rasp.RASPParser.*; +import static org.antlr.v4.runtime.Recognizer.EOF; + +public class LexicalAnalyzerImpl implements LexicalAnalyzer { + private final RASPLexer lexer; + private static final int[] tokenMap = new int[ERROR + 1]; + + static { + tokenMap[COMMENT] = Token.COMMENT; + tokenMap[COMMENT2] = Token.COMMENT; + tokenMap[EOL] = Token.WHITESPACE; + tokenMap[WS] = Token.WHITESPACE; + tokenMap[OPCODE_READ] = Token.RESERVED; + tokenMap[OPCODE_WRITE] = Token.RESERVED; + tokenMap[OPCODE_LOAD] = Token.RESERVED; + tokenMap[OPCODE_STORE] = Token.RESERVED; + tokenMap[OPCODE_ADD] = Token.RESERVED; + tokenMap[OPCODE_HALT] = Token.RESERVED; + tokenMap[OPCODE_DIV] = Token.RESERVED; + tokenMap[OPCODE_MUL] = Token.RESERVED; + tokenMap[OPCODE_SUB] = Token.RESERVED; + tokenMap[OPCODE_JMP] = Token.RESERVED; + tokenMap[OPCODE_JGTZ] = Token.RESERVED; + tokenMap[OPCODE_JZ] = Token.RESERVED; + + tokenMap[PREP_ORG] = Token.PREPROCESSOR; + tokenMap[PREP_INPUT] = Token.PREPROCESSOR; + + tokenMap[OP_CONSTANT] = Token.OPERATOR; + + tokenMap[LIT_NUMBER] = Token.LITERAL; + tokenMap[LIT_HEXNUMBER_1] = Token.LITERAL; + tokenMap[LIT_HEXNUMBER_2] = Token.LITERAL; + tokenMap[LIT_OCTNUMBER] = Token.LITERAL; + tokenMap[LIT_BINNUMBER] = Token.LITERAL; + + tokenMap[ID_IDENTIFIER] = Token.IDENTIFIER; + tokenMap[ID_LABEL] = Token.IDENTIFIER; + + tokenMap[ERROR] = Token.ERROR; + } + + + public LexicalAnalyzerImpl(RASPLexer lexer) { + this.lexer = Objects.requireNonNull(lexer); + } + + @Override + public Token nextToken() { + org.antlr.v4.runtime.Token token = lexer.nextToken(); + return new Token() { + @Override + public int getType() { + return convertLexerTokenType(token.getType()); + } + + @Override + public int getOffset() { + return token.getStartIndex(); + } + + @Override + public String getText() { + return token.getText(); + } + }; + } + + @Override + public boolean isAtEOF() { + return lexer._hitEOF; + } + + @Override + public void reset(InputStream inputStream) throws IOException { + lexer.setInputStream(CharStreams.fromStream(inputStream)); + } + + private int convertLexerTokenType(int tokenType) { + if (tokenType == EOF) { + return Token.EOF; + } + return tokenMap[tokenType]; + } +} diff --git a/plugins/compiler/raspc-rasp/src/main/java/net/emustudio/plugins/compiler/rasp/ParserErrorListener.java b/plugins/compiler/raspc-rasp/src/main/java/net/emustudio/plugins/compiler/rasp/ParserErrorListener.java new file mode 100644 index 000000000..d2da2d882 --- /dev/null +++ b/plugins/compiler/raspc-rasp/src/main/java/net/emustudio/plugins/compiler/rasp/ParserErrorListener.java @@ -0,0 +1,25 @@ +package net.emustudio.plugins.compiler.rasp; + +import net.emustudio.plugins.compiler.rasp.exceptions.SyntaxErrorException; +import org.antlr.v4.runtime.BaseErrorListener; +import org.antlr.v4.runtime.RecognitionException; +import org.antlr.v4.runtime.Recognizer; + +public class ParserErrorListener extends BaseErrorListener { + + @Override + public void syntaxError( + Recognizer recognizer, + Object offendingSymbol, + int line, + int charPositionInLine, + String msg, + RecognitionException e) { + + if (e == null) { + throw new SyntaxErrorException(line, charPositionInLine, msg); + } else { + throw new SyntaxErrorException(line, charPositionInLine, msg, e); + } + } +} diff --git a/plugins/compiler/raspc-rasp/src/main/java/net/emustudio/plugins/compiler/rasp/ParsingUtils.java b/plugins/compiler/raspc-rasp/src/main/java/net/emustudio/plugins/compiler/rasp/ParsingUtils.java new file mode 100644 index 000000000..66d3e4c3c --- /dev/null +++ b/plugins/compiler/raspc-rasp/src/main/java/net/emustudio/plugins/compiler/rasp/ParsingUtils.java @@ -0,0 +1,50 @@ +package net.emustudio.plugins.compiler.rasp; + +import org.antlr.v4.runtime.Token; + +import java.util.Locale; + +public class ParsingUtils { + + public static int parseLitHex1(Token token) { + // LIT_HEXNUMBER_1: [\-]? '0' X [0-9a-fA-F]+; + return Integer.decode(token.getText()); + } + + public static int parseLitHex2(Token token) { + // LIT_HEXNUMBER_2: [\-]? [0-9a-fA-F]+ H; + String rawText = token.getText(); + return Integer.parseInt(rawText.substring(0, rawText.length() - 1), 16); + } + + public static int parseLitOct(Token token) { + // LIT_OCTNUMBER: [\-]? [0-7]+ [oOqQ]; + String rawText = token.getText(); + return Integer.parseInt(rawText.substring(0, rawText.length() - 1), 8); + } + + public static int parseLitDec(Token token) { + // LIT_NUMBER: [\-]? [0-9]+ D? + String rawText = token.getText(); + if (rawText.endsWith("d") || rawText.endsWith("D")) { + return Integer.parseInt(rawText.substring(0, rawText.length() - 1), 10); + } else { + return Integer.parseInt(rawText, 10); + } + } + + public static int parseLitBin(Token token) { + // LIT_BINNUMBER: [01]+ B; + String rawText = token.getText(); + return Integer.parseInt(rawText.substring(0, rawText.length() - 1), 2); + } + + public static String parseLabel(Token token) { + String rawText = token.getText(); + return rawText.substring(0, rawText.length() - 1); + } + + public static String normalizeId(String id) { + return id.toLowerCase(Locale.ENGLISH); + } +} diff --git a/plugins/compiler/raspc-rasp/src/main/java/net/emustudio/plugins/compiler/rasp/ProgramParser.java b/plugins/compiler/raspc-rasp/src/main/java/net/emustudio/plugins/compiler/rasp/ProgramParser.java new file mode 100644 index 000000000..145f35d74 --- /dev/null +++ b/plugins/compiler/raspc-rasp/src/main/java/net/emustudio/plugins/compiler/rasp/ProgramParser.java @@ -0,0 +1,151 @@ +package net.emustudio.plugins.compiler.rasp; + +import net.emustudio.plugins.compiler.rasp.ast.Instruction; +import net.emustudio.plugins.compiler.rasp.ast.Label; +import net.emustudio.plugins.compiler.rasp.ast.Program; +import org.antlr.v4.runtime.Token; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +import static net.emustudio.plugins.compiler.rasp.ParsingUtils.*; +import static net.emustudio.plugins.compiler.rasp.RASPParser.*; + + +public class ProgramParser extends RASPParserBaseVisitor { + private final static Map registerOps = new HashMap<>(); + private final static Map constantOps = new HashMap<>(); + private final static Map jumpOps = new HashMap<>(); + + static { + registerOps.put(OPCODE_READ, 1); + registerOps.put(OPCODE_WRITE, 3); + registerOps.put(OPCODE_LOAD, 5); + registerOps.put(OPCODE_STORE, 6); + registerOps.put(OPCODE_ADD, 8); + registerOps.put(OPCODE_SUB, 10); + registerOps.put(OPCODE_MUL, 12); + registerOps.put(OPCODE_DIV, 14); + + constantOps.put(OPCODE_WRITE, 2); + constantOps.put(OPCODE_LOAD, 4); + constantOps.put(OPCODE_ADD, 7); + constantOps.put(OPCODE_SUB, 9); + constantOps.put(OPCODE_MUL, 11); + constantOps.put(OPCODE_DIV, 13); + + jumpOps.put(OPCODE_JMP, 15); + jumpOps.put(OPCODE_JZ, 16); + jumpOps.put(OPCODE_JGTZ, 17); + } + + private final Program program; + private int currentAddress; + + public ProgramParser(Program program) { + this.program = Objects.requireNonNull(program); + } + + public Program getProgram() { + return program; + } + + @Override + public Program visitRLine(RASPParser.RLineContext ctx) { + if (ctx.label != null) { + Token token = ctx.label; + Label label = new Label(token.getLine(), token.getCharPositionInLine(), parseLabel(token), currentAddress); + program.add(label); + } + if (ctx.statement != null) { + visitRStatement(ctx.statement); + } + return program; + } + + @Override + public Program visitROrg(ROrgContext ctx) { + this.currentAddress = parseNumber(ctx.n.n); + return program; + } + + @Override + public Program visitInstrRegister(InstrRegisterContext ctx) { + Token op = ctx.op; + int opcode = registerOps.get(op.getType()); + int operand = parseNumber(ctx.n.n); + + Instruction instruction = new Instruction( + op.getLine(), op.getCharPositionInLine(), opcode, currentAddress++, Optional.of(operand) + ); + currentAddress++; // operand + program.add(instruction); + return program; + } + + @Override + public Program visitInstrConstant(InstrConstantContext ctx) { + Token op = ctx.op; + int opcode = constantOps.get(op.getType()); + int operand = parseNumber(ctx.n.n); + + Instruction instruction = new Instruction( + op.getLine(), op.getCharPositionInLine(), opcode, currentAddress++, Optional.of(operand) + ); + currentAddress++; // operand + program.add(instruction); + return program; + } + + @Override + public Program visitInstrJump(InstrJumpContext ctx) { + Token op = ctx.op; + int opcode = jumpOps.get(op.getType()); + String id = ctx.id.getText(); + + Instruction instruction = new Instruction( + op.getLine(), op.getCharPositionInLine(), opcode, currentAddress++, id + ); + currentAddress++; // operand + program.add(instruction); + return program; + } + + @Override + public Program visitInstrNoOperand(InstrNoOperandContext ctx) { + Instruction instruction = new Instruction( + ctx.op.getLine(), ctx.op.getCharPositionInLine(), 18, currentAddress++, Optional.empty() + ); + program.add(instruction); + return program; + } + + + @Override + public Program visitRInput(RASPParser.RInputContext ctx) { + for (RNumberContext n : ctx.rNumber()) { + if (n.n != null) { + program.add(parseNumber(n.n)); + } + } + return program; + } + + public int parseNumber(Token number) { + switch (number.getType()) { + case LIT_BINNUMBER: + return parseLitBin(number); + case LIT_HEXNUMBER_1: + return parseLitHex1(number); + case LIT_HEXNUMBER_2: + return parseLitHex2(number); + case LIT_NUMBER: + return parseLitDec(number); + case LIT_OCTNUMBER: + return parseLitOct(number); + } + throw new IllegalStateException("unexpected number token type: " + number.getType()); + } +} diff --git a/plugins/compiler/raspc-rasp/src/main/java/net/emustudio/plugins/compiler/raspc/Runner.java b/plugins/compiler/raspc-rasp/src/main/java/net/emustudio/plugins/compiler/rasp/Runner.java similarity index 93% rename from plugins/compiler/raspc-rasp/src/main/java/net/emustudio/plugins/compiler/raspc/Runner.java rename to plugins/compiler/raspc-rasp/src/main/java/net/emustudio/plugins/compiler/rasp/Runner.java index 3cfee14e1..f5628b03c 100644 --- a/plugins/compiler/raspc-rasp/src/main/java/net/emustudio/plugins/compiler/raspc/Runner.java +++ b/plugins/compiler/raspc-rasp/src/main/java/net/emustudio/plugins/compiler/rasp/Runner.java @@ -17,7 +17,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package net.emustudio.plugins.compiler.raspc; +package net.emustudio.plugins.compiler.rasp; import net.emustudio.emulib.plugins.compiler.CompilerListener; import net.emustudio.emulib.plugins.compiler.CompilerMessage; @@ -39,7 +39,7 @@ public static void main(String... args) { printHelp(); return; } else if (arg.equals("--version") || arg.equals("-v")) { - System.out.println(new CompilerImpl(0L, ApplicationApi.UNAVAILABLE, PluginSettings.UNAVAILABLE).getVersion()); + System.out.println(new CompilerRASP(0L, ApplicationApi.UNAVAILABLE, PluginSettings.UNAVAILABLE).getVersion()); return; } else { break; @@ -57,10 +57,10 @@ public static void main(String... args) { } else { outputFile = inputFile; } - outputFile += ".hex"; + outputFile += ".brasp"; } - CompilerImpl compiler = new CompilerImpl(0L, ApplicationApi.UNAVAILABLE, PluginSettings.UNAVAILABLE); + CompilerRASP compiler = new CompilerRASP(0L, ApplicationApi.UNAVAILABLE, PluginSettings.UNAVAILABLE); compiler.addCompilerListener(new CompilerListener() { @Override diff --git a/plugins/compiler/raspc-rasp/src/main/java/net/emustudio/plugins/compiler/rasp/ast/Instruction.java b/plugins/compiler/raspc-rasp/src/main/java/net/emustudio/plugins/compiler/rasp/ast/Instruction.java new file mode 100644 index 000000000..8ec23469b --- /dev/null +++ b/plugins/compiler/raspc-rasp/src/main/java/net/emustudio/plugins/compiler/rasp/ast/Instruction.java @@ -0,0 +1,62 @@ +package net.emustudio.plugins.compiler.rasp.ast; + +import java.util.Objects; +import java.util.Optional; + +public class Instruction { + public final int line; + public final int column; + + public final int address; + public final int opcode; + public final Optional operand; + public final Optional id; + + public Instruction(int line, int column, int opcode, int address, Optional operand) { + this.opcode = opcode; + this.address = address; + this.operand = Objects.requireNonNull(operand); + this.id = Optional.empty(); + this.line = line; + this.column = column; + } + + public Instruction(int line, int column, int opcode, int address, String id) { + this.opcode = opcode; + this.address = address; + this.operand = Optional.empty(); + this.id = Optional.of(id); + this.line = line; + this.column = column; + } + + public Instruction(int opcode, int address, Optional operand) { + this(0, 0, opcode, address, operand); + } + + public Instruction(int opcode, int address, String id) { + this(0, 0, opcode, address, id); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Instruction that = (Instruction) o; + return address == that.address && opcode == that.opcode && operand.equals(that.operand); + } + + @Override + public int hashCode() { + return Objects.hash(address, opcode, operand); + } + + @Override + public String toString() { + return "Instruction{" + + "address=" + address + + ", opcode=" + opcode + + ", operand=" + operand + + '}'; + } +} diff --git a/plugins/compiler/raspc-rasp/src/main/java/net/emustudio/plugins/compiler/rasp/ast/Label.java b/plugins/compiler/raspc-rasp/src/main/java/net/emustudio/plugins/compiler/rasp/ast/Label.java new file mode 100644 index 000000000..823b0754c --- /dev/null +++ b/plugins/compiler/raspc-rasp/src/main/java/net/emustudio/plugins/compiler/rasp/ast/Label.java @@ -0,0 +1,54 @@ +package net.emustudio.plugins.compiler.rasp.ast; + +import net.emustudio.plugins.memory.rasp.api.RASPLabel; + +public class Label implements RASPLabel { + public final int line; + public final int column; + + private final String label; + private final int address; + + public Label(int line, int column, String text, int address) { + this.line = line; + this.column = column; + this.label = text.toUpperCase(); + this.address = address; + } + + @Override + public String getLabel() { + return label; + } + + @Override + public int getAddress() { + return address; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Label label1 = (Label) o; + + if (address != label1.address) return false; + return label.equals(label1.label); + } + + @Override + public int hashCode() { + int result = label.hashCode(); + result = 31 * result + address; + return result; + } + + @Override + public String toString() { + return "{" + + "label='" + label + '\'' + + ", address=" + address + + '}'; + } +} diff --git a/plugins/compiler/raspc-rasp/src/main/java/net/emustudio/plugins/compiler/rasp/ast/Program.java b/plugins/compiler/raspc-rasp/src/main/java/net/emustudio/plugins/compiler/rasp/ast/Program.java new file mode 100644 index 000000000..298f9ae22 --- /dev/null +++ b/plugins/compiler/raspc-rasp/src/main/java/net/emustudio/plugins/compiler/rasp/ast/Program.java @@ -0,0 +1,126 @@ +package net.emustudio.plugins.compiler.rasp.ast; + +import net.emustudio.plugins.compiler.rasp.ParsingUtils; +import net.emustudio.plugins.compiler.rasp.exceptions.CompileException; +import net.emustudio.plugins.memory.rasp.api.RASPMemoryCell; +import net.emustudio.plugins.memory.rasp.api.RASPMemoryContext; + +import java.io.*; +import java.util.*; + +public class Program { + private final List instructions = new ArrayList<>(); + private final Map labels = new HashMap<>(); + private final List inputs = new ArrayList<>(); + + public void add(Instruction instruction) { + this.instructions.add(instruction); + } + + public void add(Label label) { + String labelNorm = ParsingUtils.normalizeId(label.getLabel()); + if (labels.containsKey(labelNorm)) { + throw new CompileException(label.line, label.column, "Label is already defined!"); + } + this.labels.put(labelNorm, label); + } + + public void add(int input) { + this.inputs.add(input); + } + + public Map compile() { + Map compiled = new HashMap<>(); + + for (Instruction instruction : instructions) { + RASPMemoryCell instrCell = new RASPMemoryCellImpl(true, instruction.opcode, instruction.address); + compiled.put(instrCell.getAddress(), instrCell); + instruction.operand.ifPresent(o -> { + RASPMemoryCell cell = new RASPMemoryCellImpl(false, o, instruction.address + 1); + compiled.put(cell.getAddress(), cell); + }); + instruction.id.ifPresent(id -> { + Optional