From 6e19bb560d474736d45f5be011ff0830c7019991 Mon Sep 17 00:00:00 2001 From: Dimitar Trendafilov Date: Mon, 5 Nov 2018 23:34:00 +0200 Subject: Squashed 'spasm/' content from commit 08ee95d git-subtree-dir: spasm git-subtree-split: 08ee95d89054ba0d33697d613799a0a013aedf89 --- src/asm/assembler.cpp | 79 +++ src/asm/assembler.hpp | 36 ++ src/asm/bytecode.cpp | 74 +++ src/asm/bytecode.hpp | 87 ++++ src/asm/dfa.dot | 180 +++++++ src/asm/dfa.svg | 1367 +++++++++++++++++++++++++++++++++++++++++++++++++ src/asm/lexdump.cpp | 83 +++ src/asm/lexdump3.cpp | 62 +++ src/asm/lexer.cpp | 1123 ++++++++++++++++++++++++++++++++++++++++ src/asm/lexer.hpp | 70 +++ src/asm/lexer.re | 307 +++++++++++ src/asm/main.cpp | 21 + src/asm/symbol.cpp | 120 +++++ src/asm/symbol.hpp | 69 +++ src/asm/token.cpp | 65 +++ src/asm/token.hpp | 85 +++ src/asm/tokenizer.cpp | 51 ++ src/asm/tokenizer.hpp | 35 ++ src/dstack.cpp | 172 +++++++ src/dstack.hpp | 46 ++ src/frames.cpp | 130 +++++ src/frames.hpp | 51 ++ src/main.cpp | 33 ++ src/program.spa | 12 + src/spasm.cpp | 348 +++++++++++++ src/spasm.hpp | 13 + src/spasm_impl.hpp | 97 ++++ src/types.hpp | 17 + 28 files changed, 4833 insertions(+) create mode 100644 src/asm/assembler.cpp create mode 100644 src/asm/assembler.hpp create mode 100644 src/asm/bytecode.cpp create mode 100644 src/asm/bytecode.hpp create mode 100644 src/asm/dfa.dot create mode 100644 src/asm/dfa.svg create mode 100644 src/asm/lexdump.cpp create mode 100644 src/asm/lexdump3.cpp create mode 100644 src/asm/lexer.cpp create mode 100644 src/asm/lexer.hpp create mode 100644 src/asm/lexer.re create mode 100644 src/asm/main.cpp create mode 100644 src/asm/symbol.cpp create mode 100644 src/asm/symbol.hpp create mode 100644 src/asm/token.cpp create mode 100644 src/asm/token.hpp create mode 100644 src/asm/tokenizer.cpp create mode 100644 src/asm/tokenizer.hpp create mode 100644 src/dstack.cpp create mode 100644 src/dstack.hpp create mode 100644 src/frames.cpp create mode 100644 src/frames.hpp create mode 100644 src/main.cpp create mode 100644 src/program.spa create mode 100644 src/spasm.cpp create mode 100644 src/spasm.hpp create mode 100644 src/spasm_impl.hpp create mode 100644 src/types.hpp (limited to 'src') diff --git a/src/asm/assembler.cpp b/src/asm/assembler.cpp new file mode 100644 index 0000000..25ed377 --- /dev/null +++ b/src/asm/assembler.cpp @@ -0,0 +1,79 @@ +#include + +#include "assembler.hpp" +#include "bytecode.hpp" +#include "symbol.hpp" +#include "tokenizer.hpp" + +namespace SpasmImpl +{ +namespace ASM +{ +Assembler::Assembler(Lexer::Tokenizer& tokenizer, Bytecode_Stream& bytecode) + : _tokenizer(&tokenizer), _bytecode(&bytecode) +{ +} + +void Assembler::assemble() +{ + Lexer::Token token = _tokenizer->next_token(); + + while (token.type() != Lexer::Token::endinput) + { + switch (token.type()) + { + case Lexer::Token::ident: + assemble_identifier(token); + break; + case Lexer::Token::label: + token = _tokenizer->next_token(); + assert(token.type() == Lexer::Token::ident); + _symbols.define(token.value_str(), _bytecode->size()); + break; + case Lexer::Token::integer: + case Lexer::Token::xinteger: + _bytecode->push_integer(token.value_int()); + break; + default: + _bytecode->push_opcode((Bytecode_Stream::Opcode_t)token.type()); + } + + token = _tokenizer->next_token(); + } + + Symbol_Table::const_iterator i = _symbols.begin(); + while (i != _symbols.end()) + { + backpatch(i->second); + ++i; + } +} + +void Assembler::backpatch(const Symbol* symbol) +{ + Symbol::Positions_t::const_iterator i = symbol->positions_begin(); + while (i != symbol->positions_end()) + { + _bytecode->set_location(*i, symbol->definition()); + ++i; + } +} + +void Assembler::assemble_identifier(const Lexer::Token& token) +{ + Symbol* symbol = _symbols.insert(token.value_str(), _bytecode->size()); + _bytecode->push_location(symbol->definition()); +} + +bool compile(std::istream& istr, Bytecode_Memory& bytecode) +{ + Lexer::Tokenizer tokenizer(istr); + Assembler assembler(tokenizer, bytecode); + + assembler.assemble(); + + return true; +} + +} // namespace ASM +} // namespace SpasmImpl diff --git a/src/asm/assembler.hpp b/src/asm/assembler.hpp new file mode 100644 index 0000000..d29139b --- /dev/null +++ b/src/asm/assembler.hpp @@ -0,0 +1,36 @@ +#ifndef ASSEMBPLER_HPP +#define ASSEMBPLER_HPP + +#include + +#include "bytecode.hpp" +#include "symbol.hpp" +#include "tokenizer.hpp" + +namespace SpasmImpl +{ +namespace ASM +{ +class Assembler +{ + public: + Assembler(Lexer::Tokenizer&, Bytecode_Stream&); + + void assemble(); + + private: + void backpatch(const Symbol*); + void assemble_identifier(const Lexer::Token&); + + Lexer::Tokenizer* _tokenizer; + Bytecode_Stream* _bytecode; + + Symbol_Table _symbols; + +}; // class Assembler + +bool compile(std::istream&, Bytecode_Memory& bytecode); +} // namespace ASM +} // namespace SpasmImpl + +#endif // ASSEMBPLER_HPP diff --git a/src/asm/bytecode.cpp b/src/asm/bytecode.cpp new file mode 100644 index 0000000..27fed2b --- /dev/null +++ b/src/asm/bytecode.cpp @@ -0,0 +1,74 @@ +#include +#include +#include +#include + +#include "bytecode.hpp" + +namespace SpasmImpl +{ +namespace ASM +{ +Bytecode_Stream::~Bytecode_Stream() {} + +Bytecode_File::Bytecode_File(const std::string& filename) + : _bytecode(filename.c_str(), std::ios_base::out | std::ios_base::binary) +{ +} + +void Bytecode_File::push_byte(Bytecode_Stream::byte byte) +{ + _bytecode.put(byte); +} + +void Bytecode_File::push_opcode(Bytecode_Stream::Opcode_t opcode) +{ + push_byte(opcode); +} + +void Bytecode_File::push_integer(int number) +{ + for (size_t i = 0; i < sizeof(int); ++i) + push_byte((number >> (i << 3)) & 0xff); +} + +void Bytecode_Memory::push_byte(Bytecode_Stream::byte byte) +{ + _bytecode.push_back(byte); +} + +void Bytecode_Memory::push_opcode(Bytecode_Stream::Opcode_t opcode) +{ + push_byte(opcode); +} + +void Bytecode_Memory::push_integer(int number) +{ + for (size_t i = 0; i < sizeof(int); ++i) + push_byte((number >> (i << 3)) & 0xff); +} + +void Bytecode_Memory::set_location(size_t index, size_t location) +{ + for (size_t i = 0; i < sizeof(size_t); ++i) + _bytecode.at(index + i) = ((location >> (i << 3)) & 0xff); +} + +void Bytecode_Memory::push_location(size_t location) +{ + for (size_t i = 0; i < sizeof(size_t); ++i) + _bytecode.push_back((location >> (i << 3)) & 0xff); +} + +const Bytecode_Memory::Bytecode& Bytecode_Memory::bytecode() const +{ + return _bytecode; +} + +size_t Bytecode_Memory::size() const +{ + return _bytecode.size(); +} + +} // namespace ASM +} // namespace SpasmImpl diff --git a/src/asm/bytecode.hpp b/src/asm/bytecode.hpp new file mode 100644 index 0000000..c08da46 --- /dev/null +++ b/src/asm/bytecode.hpp @@ -0,0 +1,87 @@ +#ifndef BYTECODE_HPP +#define BYTECODE_HPP + +#include +#include +#include +#include + +namespace SpasmImpl +{ +namespace ASM +{ +class Bytecode_Stream +{ + public: + typedef unsigned char byte; + enum Opcode_t : byte + { + halt = 0, + push, + pop, + dup, + print, + read, + plus, + minus, + multiply, + divide, + modulus, + gotrue, + gofalse, + go, + call, + ret, + load, + store, + less, + lesseq + }; + + + virtual ~Bytecode_Stream(); + virtual void push_opcode(Opcode_t) = 0; + virtual void push_integer(int) = 0; + virtual void push_location(size_t) = 0; + virtual void set_location(size_t, size_t) = 0; + virtual size_t size() const = 0; +}; // class Bytecode_Stream + +class Bytecode_File : public Bytecode_Stream +{ + public: + Bytecode_File(const std::string&); + + virtual void push_opcode(Opcode_t); + virtual void push_integer(int); + + private: + Bytecode_File(const Bytecode_File&); + Bytecode_File& operator=(const Bytecode_File&); + void push_byte(Bytecode_Stream::byte); + + std::ofstream _bytecode; + +}; // class Bytecode_File + +class Bytecode_Memory : public Bytecode_Stream +{ + public: + virtual void push_opcode(Opcode_t); + virtual void push_integer(int); + virtual void push_location(size_t); + virtual void set_location(size_t, size_t); + virtual size_t size() const; + + typedef std::vector Bytecode; + const Bytecode& bytecode() const; + + private: + void push_byte(Bytecode_Stream::byte); + + std::vector _bytecode; +}; // class Bytecode_Memory +} // namespace ASM +} // namespace SpasmImpl + +#endif // BYTECODE_HPP diff --git a/src/asm/dfa.dot b/src/asm/dfa.dot new file mode 100644 index 0000000..ff38d77 --- /dev/null +++ b/src/asm/dfa.dot @@ -0,0 +1,180 @@ +/* Generated by re2c 0.16 on Thu Oct 11 01:51:09 2018 */ + +digraph re2c { + +1 -> 2 [label="[0x00]"] +1 -> 4 [label="[0x01-0x08][0x0B-0x1F][!-$][&-)][,][.][:-;][=-@][[-^][`][{-0xFF]"] +1 -> 6 [label="[0x09][ ]"] +1 -> 9 [label="[0x0A]"] +1 -> 11 [label="[%]"] +1 -> 13 [label="[*]"] +1 -> 15 [label="[+]"] +1 -> 17 [label="[-]"] +1 -> 19 [label="[/]"] +1 -> 21 [label="[0]"] +1 -> 23 [label="[1-9]"] +1 -> 25 [label="[<]"] +1 -> 27 [label="[A-Z][_][a-b][e-f][h-k][m-o][q][t-z]"] +1 -> 30 [label="[c]"] +1 -> 31 [label="[d]"] +1 -> 32 [label="[g]"] +1 -> 33 [label="[l]"] +1 -> 34 [label="[p]"] +1 -> 35 [label="[r]"] +1 -> 36 [label="[s]"] +2 -> 3 +3 [label="lexer.re:233"] +4 -> 5 +5 [label="lexer.re:239"] +6 -> 7 +7 -> 8 [label="[0x00-0x08][0x0A-0x1F][!-0xFF]"] +7 -> 6 [label="[0x09][ ]"] +8 [label="lexer.re:220"] +9 -> 10 +10 [label="lexer.re:226"] +11 -> 12 +12 [label="lexer.re:137"] +13 -> 14 +14 [label="lexer.re:123"] +15 -> 16 +16 [label="lexer.re:109"] +17 -> 18 [label="[0x00-/][1-0xFF]"] +17 -> 37 [label="[0]"] +18 [label="lexer.re:116"] +19 -> 20 +20 [label="lexer.re:130"] +21 -> 22 [label="[0x00-W][Y-w][y-0xFF]"] +21 -> 38 [label="[X][x]"] +22 [label="lexer.re:58"] +23 -> 24 +24 -> 22 [label="[0x00-/][:-0xFF]"] +24 -> 23 [label="[0-9]"] +25 -> 26 [label="[0x00-<][>-0xFF]"] +25 -> 40 [label="[=]"] +26 [label="lexer.re:198"] +27 -> 28 +28 -> 29 [label="[0x00-/][:-@][[-^][`][{-0xFF]"] +28 -> 27 [label="[0-9][A-Z][_][a-z]"] +29 [label="lexer.re:212"] +30 -> 28 [label="[0x00-`][b-0xFF]"] +30 -> 42 [label="[a]"] +31 -> 28 [label="[0x00-t][v-0xFF]"] +31 -> 43 [label="[u]"] +32 -> 28 [label="[0x00-n][p-0xFF]"] +32 -> 44 [label="[o]"] +33 -> 28 [label="[0x00-`][b-n][p-0xFF]"] +33 -> 46 [label="[a]"] +33 -> 47 [label="[o]"] +34 -> 28 [label="[0x00-n][p-q][s-t][v-0xFF]"] +34 -> 48 [label="[o]"] +34 -> 49 [label="[r]"] +34 -> 50 [label="[u]"] +35 -> 28 [label="[0x00-d][f-0xFF]"] +35 -> 51 [label="[e]"] +36 -> 28 [label="[0x00-s][u-0xFF]"] +36 -> 52 [label="[t]"] +37 -> 22 +38 -> 39 [label="[0x00-/][:-@][G-`][g-0xFF]"] +38 -> 53 [label="[0-9][A-F][a-f]"] +39 -> 22 +40 -> 41 +41 [label="lexer.re:192"] +42 -> 28 [label="[0x00-k][m-0xFF]"] +42 -> 56 [label="[l]"] +43 -> 28 [label="[0x00-o][q-0xFF]"] +43 -> 57 [label="[p]"] +44 -> 45 [label="[0x00-/][:-@][[-^][`][{-0xFF]"] +44 -> 27 [label="[0-9][A-Z][_][a-e][g-s][u-z]"] +44 -> 59 [label="[f]"] +44 -> 60 [label="[t]"] +45 [label="lexer.re:158"] +46 -> 28 [label="[0x00-a][c-0xFF]"] +46 -> 61 [label="[b]"] +47 -> 28 [label="[0x00-`][b-0xFF]"] +47 -> 62 [label="[a]"] +48 -> 28 [label="[0x00-o][q-0xFF]"] +48 -> 63 [label="[p]"] +49 -> 28 [label="[0x00-h][j-0xFF]"] +49 -> 65 [label="[i]"] +50 -> 28 [label="[0x00-r][t-0xFF]"] +50 -> 66 [label="[s]"] +51 -> 28 [label="[0x00-`][b-s][u-0xFF]"] +51 -> 67 [label="[a]"] +51 -> 68 [label="[t]"] +52 -> 28 [label="[0x00-n][p-0xFF]"] +52 -> 70 [label="[o]"] +53 -> 54 +54 -> 55 [label="[0x00-/][:-@][G-`][g-0xFF]"] +54 -> 53 [label="[0-9][A-F][a-f]"] +55 [label="lexer.re:66"] +56 -> 28 [label="[0x00-k][m-0xFF]"] +56 -> 71 [label="[l]"] +57 -> 58 [label="[0x00-/][:-@][[-^][`][{-0xFF]"] +57 -> 27 [label="[0-9][A-Z][_][a-z]"] +58 [label="lexer.re:88"] +59 -> 28 [label="[0x00-`][b-0xFF]"] +59 -> 73 [label="[a]"] +60 -> 28 [label="[0x00-q][s-0xFF]"] +60 -> 74 [label="[r]"] +61 -> 28 [label="[0x00-d][f-0xFF]"] +61 -> 75 [label="[e]"] +62 -> 28 [label="[0x00-c][e-0xFF]"] +62 -> 76 [label="[d]"] +63 -> 64 [label="[0x00-/][:-@][[-^][`][{-0xFF]"] +63 -> 27 [label="[0-9][A-Z][_][a-z]"] +64 [label="lexer.re:81"] +65 -> 28 [label="[0x00-m][o-0xFF]"] +65 -> 78 [label="[n]"] +66 -> 28 [label="[0x00-g][i-0xFF]"] +66 -> 79 [label="[h]"] +67 -> 28 [label="[0x00-c][e-0xFF]"] +67 -> 81 [label="[d]"] +68 -> 69 [label="[0x00-/][:-@][[-^][`][{-0xFF]"] +68 -> 27 [label="[0-9][A-Z][_][a-z]"] +69 [label="lexer.re:172"] +70 -> 28 [label="[0x00-q][s-0xFF]"] +70 -> 83 [label="[r]"] +71 -> 72 [label="[0x00-/][:-@][[-^][`][{-0xFF]"] +71 -> 27 [label="[0-9][A-Z][_][a-z]"] +72 [label="lexer.re:165"] +73 -> 28 [label="[0x00-k][m-0xFF]"] +73 -> 84 [label="[l]"] +74 -> 28 [label="[0x00-t][v-0xFF]"] +74 -> 85 [label="[u]"] +75 -> 28 [label="[0x00-k][m-0xFF]"] +75 -> 86 [label="[l]"] +76 -> 77 [label="[0x00-/][:-@][[-^][`][{-0xFF]"] +76 -> 27 [label="[0-9][A-Z][_][a-z]"] +77 [label="lexer.re:179"] +78 -> 28 [label="[0x00-s][u-0xFF]"] +78 -> 88 [label="[t]"] +79 -> 80 [label="[0x00-/][:-@][[-^][`][{-0xFF]"] +79 -> 27 [label="[0-9][A-Z][_][a-z]"] +80 [label="lexer.re:74"] +81 -> 82 [label="[0x00-/][:-@][[-^][`][{-0xFF]"] +81 -> 27 [label="[0-9][A-Z][_][a-z]"] +82 [label="lexer.re:95"] +83 -> 28 [label="[0x00-d][f-0xFF]"] +83 -> 90 [label="[e]"] +84 -> 28 [label="[0x00-r][t-0xFF]"] +84 -> 92 [label="[s]"] +85 -> 28 [label="[0x00-d][f-0xFF]"] +85 -> 93 [label="[e]"] +86 -> 87 [label="[0x00-/][:-@][[-^][`][{-0xFF]"] +86 -> 27 [label="[0-9][A-Z][_][a-z]"] +87 [label="lexer.re:205"] +88 -> 89 [label="[0x00-/][:-@][[-^][`][{-0xFF]"] +88 -> 27 [label="[0-9][A-Z][_][a-z]"] +89 [label="lexer.re:102"] +90 -> 91 [label="[0x00-/][:-@][[-^][`][{-0xFF]"] +90 -> 27 [label="[0-9][A-Z][_][a-z]"] +91 [label="lexer.re:186"] +92 -> 28 [label="[0x00-d][f-0xFF]"] +92 -> 95 [label="[e]"] +93 -> 94 [label="[0x00-/][:-@][[-^][`][{-0xFF]"] +93 -> 27 [label="[0-9][A-Z][_][a-z]"] +94 [label="lexer.re:144"] +95 -> 96 [label="[0x00-/][:-@][[-^][`][{-0xFF]"] +95 -> 27 [label="[0-9][A-Z][_][a-z]"] +96 [label="lexer.re:151"] +} diff --git a/src/asm/dfa.svg b/src/asm/dfa.svg new file mode 100644 index 0000000..0bd3660 --- /dev/null +++ b/src/asm/dfa.svg @@ -0,0 +1,1367 @@ + + + + + + +re2c + + +1 + +1 + + +2 + +2 + + +1->2 + + +[0x00] + + +4 + +4 + + +1->4 + + +[0x01-0x08][0x0B-0x1F][!-$][&-)][,][.][:-;][=-@][[-^][`][{-0xFF] + + +6 + +6 + + +1->6 + + +[0x09][ ] + + +9 + +9 + + +1->9 + + +[0x0A] + + +11 + +11 + + +1->11 + + +[%] + + +13 + +13 + + +1->13 + + +[*] + + +15 + +15 + + +1->15 + + +[+] + + +17 + +17 + + +1->17 + + +[-] + + +19 + +19 + + +1->19 + + +[/] + + +21 + +21 + + +1->21 + + +[0] + + +23 + +23 + + +1->23 + + +[1-9] + + +25 + +25 + + +1->25 + + +[<] + + +27 + +27 + + +1->27 + + +[A-Z][_][a-b][e-f][h-k][m-o][q][t-z] + + +30 + +30 + + +1->30 + + +[c] + + +31 + +31 + + +1->31 + + +[d] + + +32 + +32 + + +1->32 + + +[g] + + +33 + +33 + + +1->33 + + +[l] + + +34 + +34 + + +1->34 + + +[p] + + +35 + +35 + + +1->35 + + +[r] + + +36 + +36 + + +1->36 + + +[s] + + +3 + +lexer.re:233 + + +2->3 + + + + +5 + +lexer.re:239 + + +4->5 + + + + +7 + +7 + + +6->7 + + + + +10 + +lexer.re:226 + + +9->10 + + + + +12 + +lexer.re:137 + + +11->12 + + + + +14 + +lexer.re:123 + + +13->14 + + + + +16 + +lexer.re:109 + + +15->16 + + + + +18 + +lexer.re:116 + + +17->18 + + +[0x00-/][1-0xFF] + + +37 + +37 + + +17->37 + + +[0] + + +20 + +lexer.re:130 + + +19->20 + + + + +22 + +lexer.re:58 + + +21->22 + + +[0x00-W][Y-w][y-0xFF] + + +38 + +38 + + +21->38 + + +[X][x] + + +24 + +24 + + +23->24 + + + + +26 + +lexer.re:198 + + +25->26 + + +[0x00-<][>-0xFF] + + +40 + +40 + + +25->40 + + +[=] + + +28 + +28 + + +27->28 + + + + +30->28 + + +[0x00-`][b-0xFF] + + +42 + +42 + + +30->42 + + +[a] + + +31->28 + + +[0x00-t][v-0xFF] + + +43 + +43 + + +31->43 + + +[u] + + +32->28 + + +[0x00-n][p-0xFF] + + +44 + +44 + + +32->44 + + +[o] + + +33->28 + + +[0x00-`][b-n][p-0xFF] + + +46 + +46 + + +33->46 + + +[a] + + +47 + +47 + + +33->47 + + +[o] + + +34->28 + + +[0x00-n][p-q][s-t][v-0xFF] + + +48 + +48 + + +34->48 + + +[o] + + +49 + +49 + + +34->49 + + +[r] + + +50 + +50 + + +34->50 + + +[u] + + +35->28 + + +[0x00-d][f-0xFF] + + +51 + +51 + + +35->51 + + +[e] + + +36->28 + + +[0x00-s][u-0xFF] + + +52 + +52 + + +36->52 + + +[t] + + +7->6 + + +[0x09][ ] + + +8 + +lexer.re:220 + + +7->8 + + +[0x00-0x08][0x0A-0x1F][!-0xFF] + + +37->22 + + + + +39 + +39 + + +38->39 + + +[0x00-/][:-@][G-`][g-0xFF] + + +53 + +53 + + +38->53 + + +[0-9][A-F][a-f] + + +24->23 + + +[0-9] + + +24->22 + + +[0x00-/][:-0xFF] + + +41 + +lexer.re:192 + + +40->41 + + + + +28->27 + + +[0-9][A-Z][_][a-z] + + +29 + +lexer.re:212 + + +28->29 + + +[0x00-/][:-@][[-^][`][{-0xFF] + + +42->28 + + +[0x00-k][m-0xFF] + + +56 + +56 + + +42->56 + + +[l] + + +43->28 + + +[0x00-o][q-0xFF] + + +57 + +57 + + +43->57 + + +[p] + + +44->27 + + +[0-9][A-Z][_][a-e][g-s][u-z] + + +45 + +lexer.re:158 + + +44->45 + + +[0x00-/][:-@][[-^][`][{-0xFF] + + +59 + +59 + + +44->59 + + +[f] + + +60 + +60 + + +44->60 + + +[t] + + +46->28 + + +[0x00-a][c-0xFF] + + +61 + +61 + + +46->61 + + +[b] + + +47->28 + + +[0x00-`][b-0xFF] + + +62 + +62 + + +47->62 + + +[a] + + +48->28 + + +[0x00-o][q-0xFF] + + +63 + +63 + + +48->63 + + +[p] + + +49->28 + + +[0x00-h][j-0xFF] + + +65 + +65 + + +49->65 + + +[i] + + +50->28 + + +[0x00-r][t-0xFF] + + +66 + +66 + + +50->66 + + +[s] + + +51->28 + + +[0x00-`][b-s][u-0xFF] + + +67 + +67 + + +51->67 + + +[a] + + +68 + +68 + + +51->68 + + +[t] + + +52->28 + + +[0x00-n][p-0xFF] + + +70 + +70 + + +52->70 + + +[o] + + +39->22 + + + + +54 + +54 + + +53->54 + + + + +56->28 + + +[0x00-k][m-0xFF] + + +71 + +71 + + +56->71 + + +[l] + + +57->27 + + +[0-9][A-Z][_][a-z] + + +58 + +lexer.re:88 + + +57->58 + + +[0x00-/][:-@][[-^][`][{-0xFF] + + +59->28 + + +[0x00-`][b-0xFF] + + +73 + +73 + + +59->73 + + +[a] + + +60->28 + + +[0x00-q][s-0xFF] + + +74 + +74 + + +60->74 + + +[r] + + +61->28 + + +[0x00-d][f-0xFF] + + +75 + +75 + + +61->75 + + +[e] + + +62->28 + + +[0x00-c][e-0xFF] + + +76 + +76 + + +62->76 + + +[d] + + +63->27 + + +[0-9][A-Z][_][a-z] + + +64 + +lexer.re:81 + + +63->64 + + +[0x00-/][:-@][[-^][`][{-0xFF] + + +65->28 + + +[0x00-m][o-0xFF] + + +78 + +78 + + +65->78 + + +[n] + + +66->28 + + +[0x00-g][i-0xFF] + + +79 + +79 + + +66->79 + + +[h] + + +67->28 + + +[0x00-c][e-0xFF] + + +81 + +81 + + +67->81 + + +[d] + + +68->27 + + +[0-9][A-Z][_][a-z] + + +69 + +lexer.re:172 + + +68->69 + + +[0x00-/][:-@][[-^][`][{-0xFF] + + +70->28 + + +[0x00-q][s-0xFF] + + +83 + +83 + + +70->83 + + +[r] + + +54->53 + + +[0-9][A-F][a-f] + + +55 + +lexer.re:66 + + +54->55 + + +[0x00-/][:-@][G-`][g-0xFF] + + +71->27 + + +[0-9][A-Z][_][a-z] + + +72 + +lexer.re:165 + + +71->72 + + +[0x00-/][:-@][[-^][`][{-0xFF] + + +73->28 + + +[0x00-k][m-0xFF] + + +84 + +84 + + +73->84 + + +[l] + + +74->28 + + +[0x00-t][v-0xFF] + + +85 + +85 + + +74->85 + + +[u] + + +75->28 + + +[0x00-k][m-0xFF] + + +86 + +86 + + +75->86 + + +[l] + + +76->27 + + +[0-9][A-Z][_][a-z] + + +77 + +lexer.re:179 + + +76->77 + + +[0x00-/][:-@][[-^][`][{-0xFF] + + +78->28 + + +[0x00-s][u-0xFF] + + +88 + +88 + + +78->88 + + +[t] + + +79->27 + + +[0-9][A-Z][_][a-z] + + +80 + +lexer.re:74 + + +79->80 + + +[0x00-/][:-@][[-^][`][{-0xFF] + + +81->27 + + +[0-9][A-Z][_][a-z] + + +82 + +lexer.re:95 + + +81->82 + + +[0x00-/][:-@][[-^][`][{-0xFF] + + +83->28 + + +[0x00-d][f-0xFF] + + +90 + +90 + + +83->90 + + +[e] + + +84->28 + + +[0x00-r][t-0xFF] + + +92 + +92 + + +84->92 + + +[s] + + +85->28 + + +[0x00-d][f-0xFF] + + +93 + +93 + + +85->93 + + +[e] + + +86->27 + + +[0-9][A-Z][_][a-z] + + +87 + +lexer.re:205 + + +86->87 + + +[0x00-/][:-@][[-^][`][{-0xFF] + + +88->27 + + +[0-9][A-Z][_][a-z] + + +89 + +lexer.re:102 + + +88->89 + + +[0x00-/][:-@][[-^][`][{-0xFF] + + +90->27 + + +[0-9][A-Z][_][a-z] + + +91 + +lexer.re:186 + + +90->91 + + +[0x00-/][:-@][[-^][`][{-0xFF] + + +92->28 + + +[0x00-d][f-0xFF] + + +95 + +95 + + +92->95 + + +[e] + + +93->27 + + +[0-9][A-Z][_][a-z] + + +94 + +lexer.re:144 + + +93->94 + + +[0x00-/][:-@][[-^][`][{-0xFF] + + +95->27 + + +[0-9][A-Z][_][a-z] + + +96 + +lexer.re:151 + + +95->96 + + +[0x00-/][:-@][[-^][`][{-0xFF] + + + diff --git a/src/asm/lexdump.cpp b/src/asm/lexdump.cpp new file mode 100644 index 0000000..440ff1e --- /dev/null +++ b/src/asm/lexdump.cpp @@ -0,0 +1,83 @@ +#include +#include + +#include + +#include "lexer.hpp" +#include "token.hpp" + +using namespace SpasmImpl::ASM::Lexer; + +#define TOKENS \ + TOK(push) \ + TOK(pop) \ + TOK(dup) \ + TOK(print) \ + TOK(read) \ + TOK(plus) \ + TOK(minus) \ + TOK(multiply) \ + TOK(divide) \ + TOK(modulus) \ + TOK(gotrue) \ + TOK(gofalse) \ + TOK(go) \ + TOK(call) \ + TOK(ret) \ + TOK(load) \ + TOK(store) \ + TOK(label) \ + TOK(ident) \ + TOK(integer) \ + TOK(xinteger) \ + TOK(endinput) + +class TokenDumper : public TokenStream +{ + public: + TokenDumper() : end_reached(false) {} + void push_token(const Token& token) + { + Token::Token_type ttype = token.type(); + std::cout.width(8); + std::cout << token.lineno(); + std::cout << ": " << token_name[ttype]; + if (ttype == Token::integer || ttype == Token::xinteger) + std::cout << '|' << token.value_int() << '|'; + else if (ttype == Token::ident) + std::cout << '|' << token.value_str() << '|'; + std::cout << std::endl; + if (token.type() == Token::endinput) + end_reached = true; + } + + bool input_end() const { return end_reached; } + + private: + bool end_reached; + + static const char* token_name[24]; +}; + +const char* TokenDumper::token_name[24] = {"halt", +#define TOK(x) #x, + TOKENS +#undef TOK + "notused!!!"}; + +int main() +{ + Lexer lex(std::cin); + TokenDumper td; + Token token; + + lex.buffer_init(); + + while (!td.input_end()) + { + lex.read(1); + lex.tokenize(td); + } + + return 0; +} diff --git a/src/asm/lexdump3.cpp b/src/asm/lexdump3.cpp new file mode 100644 index 0000000..4e4ce9e --- /dev/null +++ b/src/asm/lexdump3.cpp @@ -0,0 +1,62 @@ +#include +#include + +#include + +#include "token.hpp" +#include "tokenizer.hpp" + +using namespace SpasmImpl::ASM::Lexer; + +#define TOKENS \ + TOK(push) \ + TOK(pop) \ + TOK(dup) \ + TOK(print) \ + TOK(read) \ + TOK(plus) \ + TOK(minus) \ + TOK(multiply) \ + TOK(divide) \ + TOK(modulus) \ + TOK(gotrue) \ + TOK(gofalse) \ + TOK(go) \ + TOK(call) \ + TOK(ret) \ + TOK(load) \ + TOK(store) \ + TOK(label) \ + TOK(ident) \ + TOK(integer) \ + TOK(xinteger) \ + TOK(endinput) + +const char* token_name[24] = {"halt", +#define TOK(x) #x, + TOKENS +#undef TOK + "notused!!!"}; + +int main() +{ + Tokenizer td(std::cin); + Token token; + + token = td.next_token(); + while (token.type() != Token::endinput) + { + token = td.next_token(); + Token::Token_type ttype = token.type(); + std::cout.width(8); + std::cout << token.lineno(); + std::cout << ": " << token_name[ttype]; + if (ttype == Token::integer || ttype == Token::xinteger) + std::cout << '|' << token.value_int() << '|'; + else if (ttype == Token::ident) + std::cout << '|' << token.value_str() << '|'; + std::cout << std::endl; + } + + return 0; +} diff --git a/src/asm/lexer.cpp b/src/asm/lexer.cpp new file mode 100644 index 0000000..ef650e4 --- /dev/null +++ b/src/asm/lexer.cpp @@ -0,0 +1,1123 @@ +/* Generated by re2c 0.13.5 on Wed Jun 25 12:01:36 2008 */ +#line 1 "lexer.re" +#include + +#include "lexer.hpp" + +#include "token.hpp" + +namespace SpasmImpl +{ +namespace ASM +{ +//! Namespace for all lexical analisys classes +namespace Lexer +{ +Lexer::Lexer(std::istream& _file, size_t _buffer_size) + : file(&_file), buffer_size(_buffer_size), state(unsigned(-1)), lineno(0) +{ + buffer = new char[buffer_size]; + cursor = limit = marker = token_start = NULL; +} + +Lexer::~Lexer() +{ + delete[] buffer; +} + +bool Lexer::tokenize(TokenStream& ts) +{ +#line 43 "lexer.re" + + switch (state) + { + default: + goto yy0; + case 0: + goto yyFillLabel0; + case 1: + goto yyFillLabel1; + case 2: + goto yyFillLabel2; + case 3: + goto yyFillLabel3; + case 4: + goto yyFillLabel4; + } + + while (1) + { +#line 48 "lexer.cpp" + + yy0: + state = (0); + if ((limit - cursor) < 8) + return true; + yyFillLabel0: + yych = *cursor; + if (yych <= '<') + { + if (yych <= ')') + { + if (yych <= '\n') + { + if (yych <= 0x00) + goto yy30; + if (yych <= 0x08) + goto yy32; + if (yych <= '\t') + goto yy26; + goto yy28; + } + else + { + if (yych <= ' ') + { + if (yych <= 0x1F) + goto yy32; + goto yy26; + } + else + { + if (yych == '%') + goto yy17; + goto yy32; + } + } + } + else + { + if (yych <= '.') + { + if (yych <= '+') + { + if (yych <= '*') + goto yy13; + goto yy11; + } + else + { + if (yych != '-') + goto yy32; + } + } + else + { + if (yych <= '0') + { + if (yych <= '/') + goto yy15; + goto yy4; + } + else + { + if (yych <= '9') + goto yy6; + if (yych <= ';') + goto yy32; + goto yy23; + } + } + } + } + else + { + if (yych <= 'f') + { + if (yych <= '_') + { + if (yych <= '@') + goto yy32; + if (yych <= 'Z') + goto yy25; + if (yych <= '^') + goto yy32; + goto yy25; + } + else + { + if (yych <= 'b') + { + if (yych <= '`') + goto yy32; + goto yy25; + } + else + { + if (yych <= 'c') + goto yy20; + if (yych <= 'd') + goto yy9; + goto yy25; + } + } + } + else + { + if (yych <= 'p') + { + if (yych <= 'k') + { + if (yych <= 'g') + goto yy19; + goto yy25; + } + else + { + if (yych <= 'l') + goto yy21; + if (yych <= 'o') + goto yy25; + goto yy7; + } + } + else + { + if (yych <= 'r') + { + if (yych <= 'q') + goto yy25; + goto yy10; + } + else + { + if (yych <= 's') + goto yy22; + if (yych <= 'z') + goto yy25; + goto yy32; + } + } + } + } + ++cursor; + if ((yych = *cursor) == '0') + goto yy99; +#line 116 "lexer.re" + { + ts.push_token(Token(Token::minus, lineno)); + token_start = cursor; + + continue; + } +#line 138 "lexer.cpp" + yy4: + yych = *(marker = ++cursor); + if (yych == 'X') + goto yy94; + if (yych == 'x') + goto yy94; + yy5: +#line 58 "lexer.re" + { + ts.push_token(Token(Token::integer, lineno, token_start, cursor)); + token_start = cursor; + + continue; + } +#line 152 "lexer.cpp" + yy6: + yych = *++cursor; + goto yy93; + yy7: + ++cursor; + if ((yych = *cursor) <= 'q') + { + if (yych == 'o') + goto yy80; + goto yy37; + } + else + { + if (yych <= 'r') + goto yy81; + if (yych == 'u') + goto yy82; + goto yy37; + } + yy8: +#line 212 "lexer.re" + { + ts.push_token(Token(Token::ident, lineno, token_start, cursor)); + token_start = cursor; + + continue; + } +#line 175 "lexer.cpp" + yy9: + yych = *++cursor; + if (yych == 'u') + goto yy77; + goto yy37; + yy10: + yych = *++cursor; + if (yych == 'e') + goto yy71; + goto yy37; + yy11: + ++cursor; +#line 109 "lexer.re" + { + ts.push_token(Token(Token::plus, lineno)); + token_start = cursor; + + continue; + } +#line 193 "lexer.cpp" + yy13: + ++cursor; +#line 123 "lexer.re" + { + ts.push_token(Token(Token::multiply, lineno)); + token_start = cursor; + + continue; + } +#line 203 "lexer.cpp" + yy15: + ++cursor; +#line 130 "lexer.re" + { + ts.push_token(Token(Token::divide, lineno)); + token_start = cursor; + + continue; + } +#line 213 "lexer.cpp" + yy17: + ++cursor; +#line 137 "lexer.re" + { + ts.push_token(Token(Token::modulus, lineno)); + token_start = cursor; + + continue; + } +#line 223 "lexer.cpp" + yy19: + yych = *++cursor; + if (yych == 'o') + goto yy58; + goto yy37; + yy20: + yych = *++cursor; + if (yych == 'a') + goto yy54; + goto yy37; + yy21: + yych = *++cursor; + if (yych == 'a') + goto yy45; + if (yych == 'o') + goto yy46; + goto yy37; + yy22: + yych = *++cursor; + if (yych == 't') + goto yy40; + goto yy37; + yy23: + ++cursor; + if ((yych = *cursor) == '=') + goto yy38; +#line 198 "lexer.re" + { + ts.push_token(Token(Token::less, lineno)); + token_start = cursor; + + continue; + } +#line 251 "lexer.cpp" + yy25: + yych = *++cursor; + goto yy37; + yy26: + ++cursor; + yych = *cursor; + goto yy35; + yy27: +#line 220 "lexer.re" + { + token_start = cursor; + + continue; + } +#line 266 "lexer.cpp" + yy28: + ++cursor; +#line 226 "lexer.re" + { + ++lineno; + token_start = cursor; + + continue; + } +#line 276 "lexer.cpp" + yy30: + ++cursor; +#line 233 "lexer.re" + { + ts.push_token(Token(Token::endinput, lineno)); + + break; + } +#line 285 "lexer.cpp" + yy32: + ++cursor; +#line 239 "lexer.re" + { + return false; + } +#line 292 "lexer.cpp" + yy34: + ++cursor; + state = (1); + if (limit <= cursor) + return true; + yyFillLabel1: + yych = *cursor; + yy35: + if (yych == '\t') + goto yy34; + if (yych == ' ') + goto yy34; + goto yy27; + yy36: + ++cursor; + state = (2); + if (limit <= cursor) + return true; + yyFillLabel2: + yych = *cursor; + yy37: + if (yych <= 'Z') + { + if (yych <= '/') + goto yy8; + if (yych <= '9') + goto yy36; + if (yych <= '@') + goto yy8; + goto yy36; + } + else + { + if (yych <= '_') + { + if (yych <= '^') + goto yy8; + goto yy36; + } + else + { + if (yych <= '`') + goto yy8; + if (yych <= 'z') + goto yy36; + goto yy8; + } + } + yy38: + ++cursor; +#line 192 "lexer.re" + { + ts.push_token(Token(Token::lesseq, lineno)); + token_start = cursor; + + continue; + } +#line 334 "lexer.cpp" + yy40: + yych = *++cursor; + if (yych != 'o') + goto yy37; + yych = *++cursor; + if (yych != 'r') + goto yy37; + yych = *++cursor; + if (yych != 'e') + goto yy37; + ++cursor; + if ((yych = *cursor) <= 'Z') + { + if (yych <= '/') + goto yy44; + if (yych <= '9') + goto yy36; + if (yych >= 'A') + goto yy36; + } + else + { + if (yych <= '_') + { + if (yych >= '_') + goto yy36; + } + else + { + if (yych <= '`') + goto yy44; + if (yych <= 'z') + goto yy36; + } + } + yy44: +#line 186 "lexer.re" + { + ts.push_token(Token(Token::store, lineno)); + token_start = cursor; + + continue; + } +#line 363 "lexer.cpp" + yy45: + yych = *++cursor; + if (yych == 'b') + goto yy50; + goto yy37; + yy46: + yych = *++cursor; + if (yych != 'a') + goto yy37; + yych = *++cursor; + if (yych != 'd') + goto yy37; + ++cursor; + if ((yych = *cursor) <= 'Z') + { + if (yych <= '/') + goto yy49; + if (yych <= '9') + goto yy36; + if (yych >= 'A') + goto yy36; + } + else + { + if (yych <= '_') + { + if (yych >= '_') + goto yy36; + } + else + { + if (yych <= '`') + goto yy49; + if (yych <= 'z') + goto yy36; + } + } + yy49: +#line 179 "lexer.re" + { + ts.push_token(Token(Token::load, lineno)); + token_start = cursor; + + continue; + } +#line 394 "lexer.cpp" + yy50: + yych = *++cursor; + if (yych != 'e') + goto yy37; + yych = *++cursor; + if (yych != 'l') + goto yy37; + ++cursor; + if ((yych = *cursor) <= 'Z') + { + if (yych <= '/') + goto yy53; + if (yych <= '9') + goto yy36; + if (yych >= 'A') + goto yy36; + } + else + { + if (yych <= '_') + { + if (yych >= '_') + goto yy36; + } + else + { + if (yych <= '`') + goto yy53; + if (yych <= 'z') + goto yy36; + } + } + yy53: +#line 205 "lexer.re" + { + ts.push_token(Token(Token::label, lineno)); + token_start = cursor; + + continue; + } +#line 421 "lexer.cpp" + yy54: + yych = *++cursor; + if (yych != 'l') + goto yy37; + yych = *++cursor; + if (yych != 'l') + goto yy37; + ++cursor; + if ((yych = *cursor) <= 'Z') + { + if (yych <= '/') + goto yy57; + if (yych <= '9') + goto yy36; + if (yych >= 'A') + goto yy36; + } + else + { + if (yych <= '_') + { + if (yych >= '_') + goto yy36; + } + else + { + if (yych <= '`') + goto yy57; + if (yych <= 'z') + goto yy36; + } + } + yy57: +#line 165 "lexer.re" + { + ts.push_token(Token(Token::call, lineno)); + token_start = cursor; + + continue; + } +#line 448 "lexer.cpp" + yy58: + ++cursor; + if ((yych = *cursor) <= '_') + { + if (yych <= '@') + { + if (yych <= '/') + goto yy59; + if (yych <= '9') + goto yy36; + } + else + { + if (yych <= 'Z') + goto yy36; + if (yych >= '_') + goto yy36; + } + } + else + { + if (yych <= 'f') + { + if (yych <= '`') + goto yy59; + if (yych <= 'e') + goto yy36; + goto yy61; + } + else + { + if (yych == 't') + goto yy60; + if (yych <= 'z') + goto yy36; + } + } + yy59: +#line 158 "lexer.re" + { + ts.push_token(Token(Token::go, lineno)); + token_start = cursor; + + continue; + } +#line 477 "lexer.cpp" + yy60: + yych = *++cursor; + if (yych == 'r') + goto yy67; + goto yy37; + yy61: + yych = *++cursor; + if (yych != 'a') + goto yy37; + yych = *++cursor; + if (yych != 'l') + goto yy37; + yych = *++cursor; + if (yych != 's') + goto yy37; + yych = *++cursor; + if (yych != 'e') + goto yy37; + ++cursor; + if ((yych = *cursor) <= 'Z') + { + if (yych <= '/') + goto yy66; + if (yych <= '9') + goto yy36; + if (yych >= 'A') + goto yy36; + } + else + { + if (yych <= '_') + { + if (yych >= '_') + goto yy36; + } + else + { + if (yych <= '`') + goto yy66; + if (yych <= 'z') + goto yy36; + } + } + yy66: +#line 151 "lexer.re" + { + ts.push_token(Token(Token::gofalse, lineno)); + token_start = cursor; + + continue; + } +#line 512 "lexer.cpp" + yy67: + yych = *++cursor; + if (yych != 'u') + goto yy37; + yych = *++cursor; + if (yych != 'e') + goto yy37; + ++cursor; + if ((yych = *cursor) <= 'Z') + { + if (yych <= '/') + goto yy70; + if (yych <= '9') + goto yy36; + if (yych >= 'A') + goto yy36; + } + else + { + if (yych <= '_') + { + if (yych >= '_') + goto yy36; + } + else + { + if (yych <= '`') + goto yy70; + if (yych <= 'z') + goto yy36; + } + } + yy70: +#line 144 "lexer.re" + { + ts.push_token(Token(Token::gotrue, lineno)); + token_start = cursor; + + continue; + } +#line 539 "lexer.cpp" + yy71: + yych = *++cursor; + if (yych == 'a') + goto yy72; + if (yych == 't') + goto yy73; + goto yy37; + yy72: + yych = *++cursor; + if (yych == 'd') + goto yy75; + goto yy37; + yy73: + ++cursor; + if ((yych = *cursor) <= 'Z') + { + if (yych <= '/') + goto yy74; + if (yych <= '9') + goto yy36; + if (yych >= 'A') + goto yy36; + } + else + { + if (yych <= '_') + { + if (yych >= '_') + goto yy36; + } + else + { + if (yych <= '`') + goto yy74; + if (yych <= 'z') + goto yy36; + } + } + yy74: +#line 172 "lexer.re" + { + ts.push_token(Token(Token::ret, lineno)); + token_start = cursor; + + continue; + } +#line 571 "lexer.cpp" + yy75: + ++cursor; + if ((yych = *cursor) <= 'Z') + { + if (yych <= '/') + goto yy76; + if (yych <= '9') + goto yy36; + if (yych >= 'A') + goto yy36; + } + else + { + if (yych <= '_') + { + if (yych >= '_') + goto yy36; + } + else + { + if (yych <= '`') + goto yy76; + if (yych <= 'z') + goto yy36; + } + } + yy76: +#line 95 "lexer.re" + { + ts.push_token(Token(Token::read, lineno)); + token_start = cursor; + + continue; + } +#line 594 "lexer.cpp" + yy77: + yych = *++cursor; + if (yych != 'p') + goto yy37; + ++cursor; + if ((yych = *cursor) <= 'Z') + { + if (yych <= '/') + goto yy79; + if (yych <= '9') + goto yy36; + if (yych >= 'A') + goto yy36; + } + else + { + if (yych <= '_') + { + if (yych >= '_') + goto yy36; + } + else + { + if (yych <= '`') + goto yy79; + if (yych <= 'z') + goto yy36; + } + } + yy79: +#line 88 "lexer.re" + { + ts.push_token(Token(Token::dup, lineno)); + token_start = cursor; + + continue; + } +#line 619 "lexer.cpp" + yy80: + yych = *++cursor; + if (yych == 'p') + goto yy90; + goto yy37; + yy81: + yych = *++cursor; + if (yych == 'i') + goto yy86; + goto yy37; + yy82: + yych = *++cursor; + if (yych != 's') + goto yy37; + yych = *++cursor; + if (yych != 'h') + goto yy37; + ++cursor; + if ((yych = *cursor) <= 'Z') + { + if (yych <= '/') + goto yy85; + if (yych <= '9') + goto yy36; + if (yych >= 'A') + goto yy36; + } + else + { + if (yych <= '_') + { + if (yych >= '_') + goto yy36; + } + else + { + if (yych <= '`') + goto yy85; + if (yych <= 'z') + goto yy36; + } + } + yy85: +#line 74 "lexer.re" + { + ts.push_token(Token(Token::push, lineno)); + token_start = cursor; + + continue; + } +#line 654 "lexer.cpp" + yy86: + yych = *++cursor; + if (yych != 'n') + goto yy37; + yych = *++cursor; + if (yych != 't') + goto yy37; + ++cursor; + if ((yych = *cursor) <= 'Z') + { + if (yych <= '/') + goto yy89; + if (yych <= '9') + goto yy36; + if (yych >= 'A') + goto yy36; + } + else + { + if (yych <= '_') + { + if (yych >= '_') + goto yy36; + } + else + { + if (yych <= '`') + goto yy89; + if (yych <= 'z') + goto yy36; + } + } + yy89: +#line 102 "lexer.re" + { + ts.push_token(Token(Token::print, lineno)); + token_start = cursor; + + continue; + } +#line 681 "lexer.cpp" + yy90: + ++cursor; + if ((yych = *cursor) <= 'Z') + { + if (yych <= '/') + goto yy91; + if (yych <= '9') + goto yy36; + if (yych >= 'A') + goto yy36; + } + else + { + if (yych <= '_') + { + if (yych >= '_') + goto yy36; + } + else + { + if (yych <= '`') + goto yy91; + if (yych <= 'z') + goto yy36; + } + } + yy91: +#line 81 "lexer.re" + { + ts.push_token(Token(Token::pop, lineno)); + token_start = cursor; + + continue; + } +#line 704 "lexer.cpp" + yy92: + ++cursor; + state = (3); + if (limit <= cursor) + return true; + yyFillLabel3: + yych = *cursor; + yy93: + if (yych <= '/') + goto yy5; + if (yych <= '9') + goto yy92; + goto yy5; + yy94: + yych = *++cursor; + if (yych <= '@') + { + if (yych <= '/') + goto yy95; + if (yych <= '9') + goto yy96; + } + else + { + if (yych <= 'F') + goto yy96; + if (yych <= '`') + goto yy95; + if (yych <= 'f') + goto yy96; + } + yy95: + cursor = marker; + goto yy5; + yy96: + ++cursor; + state = (4); + if (limit <= cursor) + return true; + yyFillLabel4: + yych = *cursor; + if (yych <= '@') + { + if (yych <= '/') + goto yy98; + if (yych <= '9') + goto yy96; + } + else + { + if (yych <= 'F') + goto yy96; + if (yych <= '`') + goto yy98; + if (yych <= 'f') + goto yy96; + } + yy98: +#line 66 "lexer.re" + { + ts.push_token(Token(Token::xinteger, lineno, token_start, cursor)); + token_start = cursor; + + continue; + } +#line 751 "lexer.cpp" + yy99: + ++cursor; + yych = *cursor; + goto yy5; +#line 243 "lexer.re" + } + return true; +} + +/*! +** Fills up the buffer when neccessary. +** Note the buffer is always filled to the maximum, not just the +** number of bytes neccessary for the lexer to continue. +** +** \param nbytes - the number of bytes +*/ + +void Lexer::buffer_grow(size_t new_size) +{ + char* new_buffer = new char[new_size]; + + std::copy(buffer, limit, new_buffer); + + token_start = new_buffer + (token_start - buffer); + cursor = new_buffer + (cursor - buffer); + limit = new_buffer + new_size; + + buffer_size = new_size; + + delete[] buffer; + buffer = new_buffer; +} + +void Lexer::read(size_t nbytes) +{ + if (token_start + nbytes > limit) + { // we need larger buffer + buffer_grow(token_start - buffer + nbytes); + } + + std::copy(token_start, limit, buffer); + cursor -= token_start - buffer; + char* old_limit = limit - (token_start - buffer); + token_start = buffer; + + file->read(old_limit, limit - old_limit); + if (file->eof()) + { + size_t bytes = file->gcount(); + std::fill(old_limit + bytes, limit, '\0'); + } +} + +void Lexer::buffer_init() +{ + file->read(buffer, buffer_size); + if (file->eof()) + { + size_t bytes = file->gcount(); + std::fill(buffer + bytes, buffer + buffer_size, '\0'); + } + + cursor = marker = token_start = buffer; + limit = buffer + buffer_size; +} + +} // namespace Lexer + +} // namespace ASM +} // namespace SpasmImpl diff --git a/src/asm/lexer.hpp b/src/asm/lexer.hpp new file mode 100644 index 0000000..ece4a07 --- /dev/null +++ b/src/asm/lexer.hpp @@ -0,0 +1,70 @@ +#ifndef LEXER_HPP +#define LEXER_HPP + +#include + +#include "token.hpp" + +namespace SpasmImpl +{ +// ! Namespace for all classes and functions dealing with assembler +namespace ASM +{ +//! Namespace for all lexical analisys classes +namespace Lexer +{ +/*! +** Class for lexers for the assembler-like language for spasm +*/ +class Lexer +{ + public: + Lexer(std::istream&, size_t = 4096); + ~Lexer(); + bool tokenize(TokenStream&); + + //! fills the buffer called when the lexer needs more data + void read(size_t = 1); + void buffer_init(); + + private: + Lexer(const Lexer&); + Lexer& operator=(const Lexer&); + + void buffer_grow(size_t); + + //! input stream for the lexer + std::istream* file; + + //! buffer for the lexer + char* buffer; + + //! size of the buffer + size_t buffer_size; + + //! cursor for re2c generated lexer + char* cursor; + + //! limit for re2c generated lexer + char* limit; + + //! marker for re2c generated lexer + char* marker; + + //! pointer to the start of the just recognized token + char* token_start; + + //! saves the state of the lexer + unsigned int state; + + //! current char + char yych; + + //! number of current line + size_t lineno; +}; +} // namespace Lexer +} // namespace ASM +} // namespace SpasmImpl + +#endif diff --git a/src/asm/lexer.re b/src/asm/lexer.re new file mode 100644 index 0000000..5e21796 --- /dev/null +++ b/src/asm/lexer.re @@ -0,0 +1,307 @@ +#include + +#include "lexer.hpp" + +#include "token.hpp" + +namespace SpasmImpl +{ + namespace ASM + { + //! Namespace for all lexical analisys classes + namespace Lexer + { + + Lexer::Lexer (std::istream & _file, size_t _buffer_size) + : file (&_file), buffer_size (_buffer_size), state (unsigned(-1)), + lineno (0) + { + buffer = new char[buffer_size]; + cursor = limit = marker = token_start = NULL; + } + + Lexer::~Lexer () + { + delete [] buffer; + } + + bool Lexer::tokenize (TokenStream &ts) + { +/*!re2c +re2c:indent:top = 5; + +re2c:define:YYFILL="return true;"; +re2c:define:YYFILL:naked=1; +re2c:define:YYCTYPE="char"; +re2c:define:YYCURSOR=cursor; +re2c:define:YYLIMIT=limit; +re2c:define:YYMARKER=marker; +re2c:define:YYGETSTATE=state; +re2c:define:YYGETSTATE:naked=1; +re2c:define:YYSETSTATE="state = "; +re2c:define:YYSETSTATE:naked=1; +*/ + +/*!getstate:re2c */ + + while (1) { + +/*!re2c + +DIGIT = [0-9] ; +XDIGIT = [0-9a-fA-F] ; + +INTEGER = "-"? "0" | ([1-9] DIGIT*) ; +XINTEGER = "0" [xX] XDIGIT+ ; +IDENTIFIER = [a-zA-Z_] [0-9a-zA-Z_]* ; + +INTEGER { + ts.push_token (Token (Token::integer, lineno, + token_start, cursor)); + token_start = cursor; + + continue; + } + +XINTEGER { + ts.push_token (Token (Token::xinteger, lineno, + token_start, cursor)); + token_start = cursor; + + continue; + } + +"push" { + ts.push_token (Token (Token::push, lineno)); + token_start = cursor; + + continue; + } + +"pop" { + ts.push_token (Token (Token::pop, lineno)); + token_start = cursor; + + continue; + } + +"dup" { + ts.push_token (Token (Token::dup, lineno)); + token_start = cursor; + + continue; + } + +"read" { + ts.push_token (Token (Token::read, lineno)); + token_start = cursor; + + continue; + } + +"print" { + ts.push_token (Token (Token::print, lineno)); + token_start = cursor; + + continue; + } + +"+" { + ts.push_token (Token (Token::plus, lineno)); + token_start = cursor; + + continue; + } + +"-" { + ts.push_token (Token (Token::minus, lineno)); + token_start = cursor; + + continue; + } + +"*" { + ts.push_token (Token (Token::multiply, lineno)); + token_start = cursor; + + continue; + } + +"/" { + ts.push_token (Token (Token::divide, lineno)); + token_start = cursor; + + continue; + } + +"%" { + ts.push_token (Token (Token::modulus, lineno)); + token_start = cursor; + + continue; + } + +"gotrue" { + ts.push_token (Token (Token::gotrue, lineno)); + token_start = cursor; + + continue; + } + +"gofalse" { + ts.push_token (Token (Token::gofalse, lineno)); + token_start = cursor; + + continue; + } + +"go" { + ts.push_token (Token (Token::go, lineno)); + token_start = cursor; + + continue; + } + +"call" { + ts.push_token (Token (Token::call, lineno)); + token_start = cursor; + + continue; + } + +"ret" { + ts.push_token (Token (Token::ret, lineno)); + token_start = cursor; + + continue; + } + +"load" { + ts.push_token (Token (Token::load, lineno)); + token_start = cursor; + + continue; + } + +"store" { + ts.push_token (Token (Token::store, lineno)); + token_start = cursor; + + continue; + } +"<=" { + ts.push_token (Token (Token::lesseq, lineno)); + token_start = cursor; + + continue; + } +"<" { + ts.push_token (Token (Token::less, lineno)); + token_start = cursor; + + continue; + } + +"label" { + ts.push_token (Token (Token::label, lineno)); + token_start = cursor; + + continue; + } + +IDENTIFIER { + ts.push_token (Token (Token::ident, lineno, token_start, + cursor)); + token_start = cursor; + + continue; + } + +[ ]+ { + token_start = cursor; + + continue; + } + +"\n" { + ++lineno; + token_start = cursor; + + continue; + } + +"\000" { + ts.push_token (Token (Token::endinput, lineno)); + + break; + } + +[^] { + return false; + } + +*/ + } + return true; + } + + /*! + ** Fills up the buffer when neccessary. + ** Note the buffer is always filled to the maximum, not just the + ** number of bytes neccessary for the lexer to continue. + ** + ** \param nbytes - the number of bytes + */ + + void Lexer::buffer_grow (size_t new_size) + { + char *new_buffer = new char[new_size]; + + std::copy (buffer, limit, new_buffer); + + token_start = new_buffer + (token_start - buffer); + cursor = new_buffer + (cursor - buffer); + limit = new_buffer + new_size; + + buffer_size = new_size; + + delete [] buffer; + buffer = new_buffer; + } + + void Lexer::read (size_t nbytes) + { + if (token_start + nbytes > limit) { // we need larger buffer + buffer_grow (token_start - buffer + nbytes); + } + + std::copy (token_start, limit, buffer); + cursor -= token_start - buffer; + char *old_limit = limit - (token_start - buffer); + token_start = buffer; + + file->read (old_limit, limit - old_limit); + if (file->eof ()) { + size_t bytes = file->gcount (); + std::fill (old_limit + bytes, limit, '\0'); + } + } + + void Lexer::buffer_init () + { + file->read (buffer, buffer_size); + if (file->eof ()) { + size_t bytes = file->gcount (); + std::fill (buffer + bytes, buffer + buffer_size, '\0'); + } + + cursor = marker = token_start = buffer; + limit = buffer + buffer_size; + } + + + } // namespace Lexer + + } // namespace ASM +} // namespace SpasmImpl + diff --git a/src/asm/main.cpp b/src/asm/main.cpp new file mode 100644 index 0000000..da8fcf6 --- /dev/null +++ b/src/asm/main.cpp @@ -0,0 +1,21 @@ +#include +#include +#include "assembler.hpp" + +int main(int argc, const char* argv[]) +{ + if (argc != 2) + return 1; + SpasmImpl::ASM::Bytecode_Memory bytecode; + SpasmImpl::ASM::compile(std::cin, bytecode); + + std::ofstream output(argv[1], std::ios_base::out | std::ios_base::binary); + + // output.write ("SPASM", 5); + auto& bc = bytecode.bytecode(); + auto size = bc.size(); + output.write(reinterpret_cast(&size), sizeof(size)); + output.write(reinterpret_cast(bc.data()), size); + + return 0; +} diff --git a/src/asm/symbol.cpp b/src/asm/symbol.cpp new file mode 100644 index 0000000..7964142 --- /dev/null +++ b/src/asm/symbol.cpp @@ -0,0 +1,120 @@ +#include +#include + +#include "symbol.hpp" + +namespace SpasmImpl +{ +namespace ASM +{ +const size_t Symbol::notdefined = ~0ull; + +Symbol::Symbol() : _definition(notdefined) {} + +Symbol::Symbol(const std::string& identifier, size_t definition) + : _identifier(identifier), _definition(definition) +{ +} + +const std::string& Symbol::identifier() const +{ + return _identifier; +} + +size_t Symbol::definition() const +{ + return _definition; +} + +void Symbol::define(size_t definition) +{ + _definition = definition; +} + +size_t Symbol::add_position(size_t position) +{ + _positions.push_back(position); + + return _definition; +} + +Symbol::Positions_t::const_iterator Symbol::positions_begin() const +{ + return _positions.begin(); +} + +Symbol::Positions_t::const_iterator Symbol::positions_end() const +{ + return _positions.end(); +} + +Symbol_Table::Symbol_Table() {} + +/*! +** Search the table for symbol by identifier +** +** \param identifier - identifier of the symbol +** +** \return pointer to the symbol if it exists or NULL otherwise. +*/ +Symbol* Symbol_Table::find(const std::string& identifier) const +{ + const_iterator i = _table.find(identifier); + return (i != _table.end()) ? i->second : NULL; +} + +/*! +** Insert symbol with identifier indentifier on position position +** of just add new position for symbol. +** \param identifier - identifer of the symbol +** \param position - position of the symbol +** +** \return pointer to the symbol +*/ +Symbol* Symbol_Table::insert(const std::string& identifier, size_t position) +{ + Symbol* psymbol = find(identifier); + if (psymbol == NULL) + { + psymbol = new Symbol(identifier); + _table.insert(std::make_pair(identifier, psymbol)); + } + psymbol->add_position(position); + + return psymbol; +} + +/*! +** define symbol with identifier indentifier +** If the symbol already exists its definition is changed +** \param identifier - identifer of the symbol +** \param definition - definition of the symbol +** +** \return pointer to the symbol +*/ +Symbol* Symbol_Table::define(const std::string& identifier, size_t definition) +{ + Symbol* psymbol = find(identifier); + if (psymbol == NULL) + { + psymbol = new Symbol(identifier, definition); + _table.insert(std::make_pair(identifier, psymbol)); + } + else + psymbol->define(definition); + + return psymbol; +} + +Symbol_Table::const_iterator Symbol_Table::begin() const +{ + return _table.begin(); +} + +Symbol_Table::const_iterator Symbol_Table::end() const +{ + return _table.end(); +} + +} // namespace ASM +} // namespace SpasmImpl diff --git a/src/asm/symbol.hpp b/src/asm/symbol.hpp new file mode 100644 index 0000000..6ce4dbf --- /dev/null +++ b/src/asm/symbol.hpp @@ -0,0 +1,69 @@ +#ifndef SYMBOL_HPP +#define SYMBOL_HPP + +#include +#include +#include + +namespace SpasmImpl +{ +namespace ASM +{ +//! Represents symbol table entry for the assembler +class Symbol +{ + public: + typedef std::list Positions_t; + static const size_t notdefined; + Symbol(); + Symbol(const std::string&, size_t = 0); + + const std::string& identifier() const; + size_t definition() const; + + void define(size_t); + + size_t add_position(size_t); + + Positions_t::const_iterator positions_begin() const; + Positions_t::const_iterator positions_end() const; + + private: + //! identifier of the symbol + std::string _identifier; + + //! position of definition of the identifier + size_t _definition; + + //! list of uses of the identifier (positions) + Positions_t _positions; +}; + +//! This is the symbol table of the assembler +class Symbol_Table +{ + private: + typedef std::map Table_t; + typedef Table_t::iterator iterator; + + public: + Symbol_Table(); + typedef Table_t::const_iterator const_iterator; + + Symbol* find(const std::string&) const; + Symbol* insert(const std::string&, size_t); + Symbol* define(const std::string&, size_t); + + const_iterator begin() const; + const_iterator end() const; + + private: + Symbol_Table(const Symbol_Table&); + Symbol_Table& operator=(const Symbol_Table&); + + Table_t _table; +}; +} // namespace ASM +} // namespace SpasmImpl + +#endif // SYMBOL_HPP diff --git a/src/asm/token.cpp b/src/asm/token.cpp new file mode 100644 index 0000000..6c88bef --- /dev/null +++ b/src/asm/token.cpp @@ -0,0 +1,65 @@ +#include +#include +#include +#include + +#include "token.hpp" + +namespace SpasmImpl +{ +namespace ASM +{ +namespace Lexer +{ +Token::Token(Token_type type) + : _type(type), _lineno(0), _value_int(0), _value_str("") +{ +} + +Token::Token(Token::Token_type type, + size_t lineno, + const char* start, + const char* end) + : _type(type), _lineno(lineno) +{ + if (_type == integer || _type == xinteger) + { + _value_int = strtol(start, NULL, 0); + } + else if (_type == ident) + { + if (end) + { + _value_str = std::string(start, end); + } + else + { + _value_str = std::string(start); + } + } +} + +Token::Token_type Token::type() const +{ + return _type; +} + +int Token::value_int() const +{ + return _value_int; +} + +const std::string& Token::value_str() const +{ + return _value_str; +} + +size_t Token::lineno() const +{ + return _lineno; +} + +} // namespace Lexer + +} // namespace ASM +} // namespace SpasmImpl diff --git a/src/asm/token.hpp b/src/asm/token.hpp new file mode 100644 index 0000000..4ef7ce0 --- /dev/null +++ b/src/asm/token.hpp @@ -0,0 +1,85 @@ +#ifndef TOKEN_HPP +#define TOKEN_HPP + +#include + +#define TOKENS \ + TOK(push) \ + TOK(pop) \ + TOK(dup) \ + TOK(print) \ + TOK(read) \ + TOK(plus) \ + TOK(minus) \ + TOK(multiply) \ + TOK(divide) \ + TOK(modulus) \ + TOK(gotrue) \ + TOK(gofalse) \ + TOK(go) \ + TOK(call) \ + TOK(ret) \ + TOK(load) \ + TOK(store) \ + TOK(less) \ + TOK(lesseq) \ + TOK(label) \ + TOK(ident) \ + TOK(integer) \ + TOK(xinteger) \ + TOK(endinput) + +namespace SpasmImpl +{ +namespace ASM +{ +namespace Lexer +{ +class Token +{ + public: + enum Token_type + { + halt = 0, +#define TOK(x) x, + TOKENS +#undef TOK + notused + }; + + Token(Token_type = notused); + + Token(Token_type, size_t, const char* = NULL, const char* = NULL); + + Token_type type() const; + + size_t lineno() const; + + int value_int() const; + + const std::string& value_str() const; + + private: + Token_type _type; + + size_t _lineno; + + int _value_int; + + std::string _value_str; +}; + +class TokenStream +{ + public: + virtual void push_token(const Token&) = 0; + virtual ~TokenStream(){}; +}; + +} // namespace Lexer +} // namespace ASM +} // namespace SpasmImpl + +#undef TOKENS + +#endif diff --git a/src/asm/tokenizer.cpp b/src/asm/tokenizer.cpp new file mode 100644 index 0000000..8694ccf --- /dev/null +++ b/src/asm/tokenizer.cpp @@ -0,0 +1,51 @@ +#include +#include +#include + +#include "lexer.hpp" +#include "token.hpp" +#include "tokenizer.hpp" + +namespace SpasmImpl +{ +namespace ASM +{ +namespace Lexer +{ +Tokenizer::Tokenizer(std::istream& istr) + : _lexer(new Lexer(istr, 1024)), end_input(false) +{ + _lexer->buffer_init(); +} + +void Tokenizer::push_token(const Token& token) +{ + _tokens.push(token); + if (token.type() == Token::endinput) + end_input = true; +} + +Token Tokenizer::next_token() +{ + Token t(Token::endinput); + if (!_tokens.empty()) + { + t = _tokens.front(); + _tokens.pop(); + } + else if (!end_input) + { + while (_tokens.empty()) + { + _lexer->read(); + _lexer->tokenize(*this); + } + t = _tokens.front(); + _tokens.pop(); + } + return t; +} + +} // namespace Lexer +} // namespace ASM +} // namespace SpasmImpl diff --git a/src/asm/tokenizer.hpp b/src/asm/tokenizer.hpp new file mode 100644 index 0000000..1155db7 --- /dev/null +++ b/src/asm/tokenizer.hpp @@ -0,0 +1,35 @@ +#ifndef TOKENIZER_HPP +#define TOKENIZER_HPP + +#include +#include +#include + +#include "lexer.hpp" +#include "token.hpp" + +namespace SpasmImpl +{ +namespace ASM +{ +namespace Lexer +{ +class Tokenizer : public TokenStream +{ + public: + Tokenizer(std::istream&); + + virtual void push_token(const Token&); + Token next_token(); + + private: + std::unique_ptr _lexer; + std::queue _tokens; + bool end_input; +}; // class Tokenizer +} // namespace Lexer + +} // namespace ASM +} // namespace SpasmImpl + +#endif diff --git a/src/dstack.cpp b/src/dstack.cpp new file mode 100644 index 0000000..93592ed --- /dev/null +++ b/src/dstack.cpp @@ -0,0 +1,172 @@ +#include "dstack.hpp" + +#include + +namespace SpasmImpl +{ +Dstack::Dstack() : bottom(NULL), tos(NULL), s_size(0) {} + +Dstack::Dstack(const Dstack& ds) : s_size(ds.s_size) +{ + copy_dstack(ds); +} + +Dstack& Dstack::operator=(const Dstack& ds) +{ + if (this != &ds) + { + if (bottom) + delete[] bottom; + s_size = ds.s_size; + copy_dstack(ds); + } + return *this; +} + +Dstack::~Dstack() +{ + if (bottom) + delete[] bottom; +} + +/** + * Makes the stack occupy new_size bytes + * \param new_size new size for the stack + */ +void Dstack::reserve(size_t new_size) +{ + size_t offset = tos - bottom; + byte* new_bottom = new byte[new_size]; + if (bottom) + { + std::copy(bottom, bottom + std::min(s_size, new_size), new_bottom); + delete[] bottom; + } + bottom = new_bottom; + tos = new_bottom + offset; + s_size = new_size; +} + +/*! +** Pushes x in the stack. +** If the stack is full its size is doubled. +** \param x data_t object to be pushed on the stack +*/ +void Dstack::push(data_t x) +{ + if (bottom + s_size < tos + sizeof(data_t)) + reserve((s_size ? s_size : sizeof(data_t)) * 2); + *((data_t*)tos) = x; + tos += sizeof(data_t); +} + +/*! +** Pushes the object of size x_size bytes pointed by x in the stack. +** If the stack is full its size is doubled (or more is x_size is greater +** than the size of the stack). +** \param x pointer to the element to be pushed on the stack +** \param x_size size of the element +*/ +void Dstack::push(byte* x, size_t x_size) +{ + if (bottom + s_size < tos + x_size) + reserve(s_size + ((s_size > x_size) ? s_size : x_size)); + std::copy(x, x + x_size, tos); + tos += x_size; +} + +/*! +** Pops an object of size x_size from the stack. +** The object is written to the place pointed by x. +** +** \param x pointer to the place for popping +** \param x_size size of the object to be popped +*/ +void Dstack::pop(void* x, size_t x_size) +{ + if (tos - x_size >= bottom) + { + std::copy(tos - x_size, tos, (byte*)x); + tos -= x_size; + } +} + +/*! +** \return a single data_t object, popping it from the stack. +*/ +data_t Dstack::pop() +{ + data_t x{}; + if (tos - sizeof(data_t) >= bottom) + { + tos -= sizeof(data_t); + x = *((data_t*)tos); + } + return x; +} + +/*! +** \return copy of the data_t object that is on top of the stack +*/ +data_t Dstack::top() const +{ + data_t x{}; + if (tos - sizeof(data_t) >= bottom) + { + x = *((data_t*)(tos - sizeof(data_t))); + } + return x; +} + +/*! +** \return the size of the stack in bytes +*/ +size_t Dstack::size() const +{ + return tos - bottom; +} + +/*! +** \return the space reserved for the stack in bytes +*/ +size_t Dstack::reserved() const +{ + return s_size; +} + +/*! +** Frees unused memory. Note that it is not always possible because memory +** management is done with new / delete +*/ + +void Dstack::strip_memory() +{ + reserve(size()); +} + +/*! +** \return true if the stack is empty +*/ +bool Dstack::empty() const +{ + return tos - bottom == 0; +} + +/*! +** Creates a copy of ds +** +** \param ds data stack to be copied +*/ +void Dstack::copy_dstack(const Dstack& ds) +{ + if (ds.bottom) + { + bottom = new byte[ds.s_size]; + tos = bottom + (ds.tos - ds.bottom); + std::copy(ds.bottom, ds.tos, bottom); + } + else + bottom = tos = NULL; +} + +} // namespace SpasmImpl diff --git a/src/dstack.hpp b/src/dstack.hpp new file mode 100644 index 0000000..850b66b --- /dev/null +++ b/src/dstack.hpp @@ -0,0 +1,46 @@ +#ifndef DSTACK_HPP +#define DSTACK_HPP + +#include "types.hpp" + +namespace SpasmImpl +{ +//! class representing the data stack + +/*! The class is quite simple stack. It is byte-orientatied - every push +** and pop is done in bytes with no taking care of any byte order! +*/ +class Dstack +{ + public: + Dstack(); + Dstack(const Dstack&); + Dstack& operator=(const Dstack&); + ~Dstack(); + void push(data_t); + void push(byte*, size_t); + void pop(void*, size_t); + data_t pop(); + data_t top() const; + + void reserve(size_t); + void strip_memory(); + size_t size() const; + size_t reserved() const; + bool empty() const; + + private: + void copy_dstack(const Dstack&); + + //! pointer to the bottom of the stack + byte* bottom; + + //! pointer to the top of the stack + byte* tos; + + //! size of the stack in bytes + size_t s_size; +}; +} // namespace SpasmImpl + +#endif // #ifndef DSTACK_HPP diff --git a/src/frames.cpp b/src/frames.cpp new file mode 100644 index 0000000..8b7c891 --- /dev/null +++ b/src/frames.cpp @@ -0,0 +1,130 @@ +#include + +#include "types.hpp" + +#include "frames.hpp" + +namespace SpasmImpl +{ +FrameStack::FrameStack() : base_ptr(NULL), frame_ptr(NULL), g_size(0), c_size(0) +{ +} + +FrameStack::FrameStack(const FrameStack& fs) + : g_size(fs.g_size), c_size(fs.c_size), fstack(fs.fstack) +{ + copy_fstack(fs); +} + +FrameStack::~FrameStack() +{ + delete_fstack(); +} + +FrameStack& FrameStack::operator=(const FrameStack& fs) +{ + if (this != &fs) + { + delete_fstack(); + copy_fstack(fs); + g_size = fs.g_size; + c_size = fs.c_size; + fstack = fs.fstack; + } + return *this; +} + +/*! +** Deletes the frame stack +*/ +void FrameStack::delete_fstack() +{ + if (base_ptr) + delete[] base_ptr; +} + +/*! +** Copies the frame stack fs +** +** \param fs - the frame stack to be copied +*/ +void FrameStack::copy_fstack(const FrameStack& fs) +{ + base_ptr = new data_t[fs.g_size]; + std::copy(fs.base_ptr, fs.frame_ptr + fs.c_size, base_ptr); + frame_ptr = base_ptr + (fs.frame_ptr - fs.base_ptr); +} + +/*! +** Returns a data_t object from the current frame +** +** \param index - index of the data_t object +** \return const reference to the object +*/ +const data_t& FrameStack::operator[](size_t index) const +{ + return frame_ptr[index]; +} + +/*! +** Returns a data_t object from the current frame +** +** \param index - index of the data_t object +** \return reference to the object +*/ +data_t& FrameStack::operator[](size_t index) +{ + return frame_ptr[index]; +} + +/*! +** Removes the current frame from the stack. +** It is called when a function does return. +*/ +void FrameStack::pop_frame() +{ + if (fstack.empty()) + { + frame_ptr = base_ptr; + c_size = 0; + } + else + { + data_t* new_frame_ptr = base_ptr + fstack.top(); + fstack.pop(); + c_size = frame_ptr - new_frame_ptr; + frame_ptr = new_frame_ptr; + } +} + +/*! +** Creates (pushes on the stack) new frame of fsize data_t objects +** +** \param fsize - size in data_t objects of the new frame +*/ +void FrameStack::new_frame(size_t fsize) +{ + if (frame_ptr + c_size + fsize > base_ptr + g_size) + { + size_t new_size = g_size + std::max(g_size, c_size + fsize); + data_t* new_base = new data_t[new_size]; + if (base_ptr) + { + std::copy(base_ptr, frame_ptr + c_size, new_base); + delete[] base_ptr; + } + frame_ptr = new_base + (frame_ptr - base_ptr); + base_ptr = new_base; + } + fstack.push(frame_ptr - base_ptr); + c_size = fsize; +} + +/*! +** \return true if the stack is empty +*/ +bool FrameStack::empty() const +{ + return frame_ptr == base_ptr && c_size == 0; +} +} // namespace SpasmImpl diff --git a/src/frames.hpp b/src/frames.hpp new file mode 100644 index 0000000..14d1d14 --- /dev/null +++ b/src/frames.hpp @@ -0,0 +1,51 @@ +#ifndef FRAMES_HPP +#define FRAMES_HPP + +#include + +#include "types.hpp" + +namespace SpasmImpl +{ +//! Class representing the stack frame for a function +/*! +** The class is implemented as a simple stack of frames while each frame +** is built up by a number of data_t objects +*/ +class FrameStack +{ + public: + FrameStack(); + FrameStack(const FrameStack&); + ~FrameStack(); + FrameStack& operator=(const FrameStack&); + + const data_t& operator[](size_t) const; + data_t& operator[](size_t); + void pop_frame(); + void new_frame(size_t); + bool empty() const; + + private: + //! pointer to the base of the stack + data_t* base_ptr; + + //! pointer to the beginning of the current frame + data_t* frame_ptr; + + //! size of the whole stack in data_t objects + size_t g_size; + + //! size of the current frame in data_t objects + size_t c_size; + + //! stack of the offsets between frames in the stack. + std::stack fstack; + + void copy_fstack(const FrameStack&); + void delete_fstack(); +}; + +} // namespace SpasmImpl + +#endif // #ifndef FRAMES_HPP diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..17bb322 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,33 @@ +#include +#include + +#include +#include "spasm.hpp" + +int main(int argc, const char* argv[]) +{ + if (argc != 2) + return 1; + + std::ifstream input(argv[1], std::ios_base::in | std::ios_base::binary); + + size_t len; + + input.read((char*)&len, sizeof(len)); + + std::unique_ptr bytecode(new Spasm::byte[len]); + + input.read((char*)bytecode.get(), len); + + for (size_t i = 0; i < len; ++i) + std::cout << std::hex << (int)bytecode[i] << ' '; + std::cout << std::endl; + + Spasm::Spasm vm(len, bytecode.get()); + + vm.run(); + + std::cout << std::endl; + + return 0; +} diff --git a/src/program.spa b/src/program.spa new file mode 100644 index 0000000..d92456a --- /dev/null +++ b/src/program.spa @@ -0,0 +1,12 @@ +read +label next +dup +push 1 +< +gotrue bye +push 1 +- +dup +print +go next +label bye diff --git a/src/spasm.cpp b/src/spasm.cpp new file mode 100644 index 0000000..e67c975 --- /dev/null +++ b/src/spasm.cpp @@ -0,0 +1,348 @@ +#include +#include + +#include "spasm.hpp" + +namespace SpasmImpl +{ +PC_t Spasm::op_size = 21; +// Be sure the same number is on the previous and next lines +Operation Spasm::operations[] = {NULL, + &Spasm::push, + &Spasm::pop, + &Spasm::dup, + &Spasm::print, + &Spasm::read, + &Spasm::plus, + &Spasm::minus, + &Spasm::multiply, + &Spasm::divide, + &Spasm::modulus, + &Spasm::gotrue, + &Spasm::gofalse, + &Spasm::go, + &Spasm::call, + &Spasm::ret, + &Spasm::load, + &Spasm::store, + &Spasm::less, + &Spasm::lesseq}; + +Spasm::Spasm() + : pc(0), bc_size(0), bytecode(NULL), istr(&std::cin), ostr(&std::cout) +{ +} + +/*! +** Constructs new Spasm object +** +** \param _bc_size - size/length of the bytecode +** \param _bytecode - the bytecode of the program +** \param _istr - input stream for the machine +** \param _ostr - output stream for the machine +*/ +Spasm::Spasm(PC_t _bc_size, + const byte* _bytecode, + std::istream& _istr, + std::ostream& _ostr) + : pc(0), bc_size(_bc_size), istr(&_istr), ostr(&_ostr) +{ + bytecode = new byte[bc_size]; + std::copy(_bytecode, _bytecode + bc_size, bytecode); +} + +Spasm::Spasm(const Spasm& m) + : pc(m.pc), + bc_size(m.bc_size), + data_stack(m.data_stack), + return_stack(m.return_stack), + frame(m.frame), + istr(m.istr), + ostr(m.ostr) +{ + copybc(m); +} + +Spasm::~Spasm() +{ + deleteobj(); +} + +Spasm& Spasm::operator=(const Spasm& m) +{ + if (this != &m) + { + deleteobj(); + data_stack = m.data_stack; + return_stack = m.return_stack; + frame = m.frame; + istr = m.istr; + ostr = m.ostr; + copybc(m); + } + return *this; +} + +/*! +** Copies the bytecode of another machine m +** +** \param m - the machine from the bytecode is copied +*/ + +void Spasm::copybc(const Spasm& m) +{ + bytecode = new byte[bc_size]; + std::copy(m.bytecode, m.bytecode + bc_size, bytecode); +} + +/*! +** Deletes the machine +*/ +void Spasm::deleteobj() +{ + if (bytecode) + delete[] bytecode; +} + +/*! +** Returns the dstack, so that it can be examined after execution +** +** \return const reference to the data stack +*/ +const Dstack& Spasm::get_dstack() const +{ + return data_stack; +} + +/*! +** Starts the machine. The machine stops if it reaches an invalid opcode +** or opcode 0 or the pc reaches beyond the end of the bytecode. +*/ +void Spasm::run() +{ + while (pc < bc_size) + { + if (bytecode[pc] > 0 && bytecode[pc] < op_size) + (this->*operations[bytecode[pc]])(); + else + { + std::cerr << "opcode got to " << int(bytecode[pc]) << std::endl; + break; + } + ++pc; + } +} + +/*! +** Pushes the next data_t object on the data stack +*/ +void Spasm::push() +{ + ++pc; + data_stack.push(*reinterpret_cast(bytecode + pc)); + pc += sizeof(data_t) - 1; +} + +/*! +** Pops a data_t object from the data stack. +*/ +void Spasm::pop() +{ + data_stack.pop(); +} + +/*! +** Dups the data_t object on top of the data stack. +*/ +void Spasm::dup() +{ + data_stack.push(data_stack.top()); +} + +/*! +** Reads a data_t object from the input stream and pushes it on the data +** stack. +*/ +void Spasm::read() +{ + data_t x; + *istr >> x; + data_stack.push(x); +} + +/*! +** Pops a data_t object from the data stack and prints it on the output +** stream. +*/ +void Spasm::print() +{ + *ostr << data_stack.pop(); +} + +/*! +** Pops two data objects from the data stack and pushes their sum on the +** data stack. +*/ +void Spasm::plus() +{ + data_t x, y = data_stack.pop(); + x = data_stack.pop(); + data_stack.push(x + y); +} + +/*! +** Pops two data objects from the data stack and pushes their difference +** on the data stack. +*/ +void Spasm::minus() +{ + data_t x, y = data_stack.pop(); + x = data_stack.pop(); + data_stack.push(x - y); +} + +/*! +** Pops two data objects from the data stack and pushes their product +** on the data stack. +*/ +void Spasm::multiply() +{ + data_t x, y = data_stack.pop(); + x = data_stack.pop(); + data_stack.push(x * y); +} + +/*! +** Pops two data objects from the data stack and pushes their division +** on the data stack. +*/ +void Spasm::divide() +{ + data_t x, y = data_stack.pop(); + x = data_stack.pop(); + data_stack.push(x / y); +} + +/*! +** Pops two data objects from the data stack and pushes their modulus +** on the data stack. +*/ +void Spasm::modulus() +{ + data_t x, y = data_stack.pop(); + x = data_stack.pop(); + data_stack.push(x % y); +} + +/*! +** Conditional execution. +** If the data_t object on top of the data stack evaluates to true +** execution continues from the place pointed by number in the next bytes +*/ +void Spasm::gotrue() +{ + ++pc; + if (data_stack.pop()) + pc = *(reinterpret_cast(bytecode + pc)) - 1; + else + pc += sizeof(size_t) - 1; +} + +/*! +** Conditional execution. +** If the data_t object on top of the data stack evaluates to false +** execution continues from the place pointed by number in the next bytes +*/ +void Spasm::gofalse() +{ + ++pc; + if (!data_stack.pop()) + pc = *(reinterpret_cast(bytecode + pc)) - 1; + else + pc += sizeof(size_t) - 1; +} + +/*! +** Unconditional execution. +** Execution continues from the opcode pointed by the next bytes +*/ +void Spasm::go() +{ + ++pc; + pc = *(reinterpret_cast(bytecode + pc)) - 1; +} + +/*! +** Function call. A new frame with the specified size is created, the +** return address is saved in the return stack and the new pc is loaded. +*/ +void Spasm::call() +{ + size_t fs; // new frame size + return_stack.push(pc); // using the ++pc in run() + data_stack.pop(&fs, sizeof(fs)); + data_stack.pop(&pc, sizeof(pc)); + frame.new_frame(fs); + --pc; // the ++pc in run() +} + +/*! +** Function return. The frame of the current function is destroyed and the +** saved return address is loaded in the pc +*/ +void Spasm::ret() +{ + pc = return_stack.top(); + return_stack.pop(); + frame.pop_frame(); +} + +/*! +** Local variable with index top of the data stack is pushed on top of the +** data stack. +*/ +void Spasm::load() +{ + size_t offset; + + data_stack.pop(&offset, sizeof(offset)); + data_stack.push(frame[offset]); +} + +/*! +** Local variable is assigned value +*/ +void Spasm::store() +{ + data_t value = data_stack.pop(); + size_t offset; + + data_stack.pop(&offset, sizeof(offset)); + frame[offset] = value; +} + +/*! +** Compares the values on top of the stack. Pushes 1 if the value before +** the top of the stack is less than that on top of the stack. +*/ +void Spasm::less() +{ + data_t y = data_stack.pop(); + data_t x = data_stack.pop(); + + data_stack.push(x < y); +} + +/*! +** Compares the values on top of the stack. Pushes 1 if the value before +** the top of the stack is less than or equal to that on top of the stack. +*/ +void Spasm::lesseq() +{ + data_t y = data_stack.pop(); + data_t x = data_stack.pop(); + + data_stack.push(x <= y); +} + +} // namespace SpasmImpl diff --git a/src/spasm.hpp b/src/spasm.hpp new file mode 100644 index 0000000..7468bd8 --- /dev/null +++ b/src/spasm.hpp @@ -0,0 +1,13 @@ +#ifndef SPASM_HPP +#define SPASM_HPP + +#include "spasm_impl.hpp" + +namespace Spasm +{ +using SpasmImpl::byte; +using SpasmImpl::PC_t; +using SpasmImpl::Spasm; +} // namespace Spasm + +#endif // #ifndef SPASM_HPP diff --git a/src/spasm_impl.hpp b/src/spasm_impl.hpp new file mode 100644 index 0000000..78c5d08 --- /dev/null +++ b/src/spasm_impl.hpp @@ -0,0 +1,97 @@ +#ifndef SPASM_IMPL_HPP +#define SPASM_IMPL_HPP + +#include + +#include "dstack.hpp" +#include "frames.hpp" +#include "types.hpp" + +namespace SpasmImpl +{ +//! The Abstract Stack Machine +/*! +** The machine contains a data stack for operations and control flow and +** frame stack for function calls (including parameter passing) and local +** variables and return stack for the addresses of the returns. +*/ +class Spasm +{ + public: + Spasm(); + Spasm(PC_t, + const byte*, + std::istream& = std::cin, + std::ostream& = std::cout); + Spasm(const Spasm&); + ~Spasm(); + Spasm& operator=(const Spasm&); + + void run(); + + const Dstack& get_dstack() const; + + private: + //! Operations (opcodes) of the machine + static Operation operations[]; + + //! number of operations + static PC_t op_size; + + //! Program counter - points the current opcode + PC_t pc; + + //! size of the program bytecode + PC_t bc_size; + + //! bytecode of the program + byte* bytecode; + + //! Data stack for arithmetic operations and control flow + Dstack data_stack; + + //! Return stack for the addresses of the returns + Rstack_t return_stack; + + //! Stack for the frame of the functions + FrameStack frame; + + //! Input stream for read () operation + std::istream* istr; + + //! Output stream for print () opertion + std::ostream* ostr; + + void push(); + void pop(); + void dup(); + + void print(); + void read(); + + void plus(); + void minus(); + void multiply(); + void divide(); + void modulus(); + + void gotrue(); + void gofalse(); + void go(); + + void call(); + void ret(); + + void load(); + void store(); + + void less(); + void lesseq(); + + // private method for copying and deleting an object + void copybc(const Spasm&); + void deleteobj(); +}; + +} // namespace SpasmImpl +#endif // #ifndef SPASM_IMPL_HPP diff --git a/src/types.hpp b/src/types.hpp new file mode 100644 index 0000000..0d86cab --- /dev/null +++ b/src/types.hpp @@ -0,0 +1,17 @@ +#ifndef TYPES_HPP +#define TYPES_HPP + +#include +#include + +namespace SpasmImpl +{ +typedef unsigned char byte; +typedef size_t PC_t; +typedef int data_t; +typedef std::stack Rstack_t; + +class Spasm; +typedef void (Spasm::*Operation)(); +} // namespace SpasmImpl +#endif // ----- #ifndef TYPES_HPP -- cgit v1.3.1-23-gd82d