bsm 0.1.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile +13 -1
- data/bsm.gemspec +23 -22
- data/bsm2/Makefile +104 -0
- data/bsm2/src/bsm2/main.cpp +50 -0
- data/bsm2/src/c_api.cpp +127 -0
- data/bsm2/src/c_api.h +41 -0
- data/bsm2/src/converter.h +58 -0
- data/bsm2/src/error_info.h +15 -0
- data/bsm2/src/lexer.cpp +276 -0
- data/bsm2/src/lexer.h +59 -0
- data/bsm2/src/msb2/README.md +2 -0
- data/bsm2/src/msb2/main.cpp +38 -0
- data/bsm2/src/processor.cpp +31 -0
- data/bsm2/src/processor.h +23 -0
- data/exe/bsm +5 -9
- data/ext/bsm/internal/bsm.c +119 -0
- data/ext/bsm/internal/extconf.rb +25 -2
- data/lib/bsm/internal.so +0 -0
- data/lib/bsm/version.rb +3 -1
- data/lib/bsm.rb +21 -2
- metadata +31 -74
- data/lib/bsm/bsm_parse/internal.bundle +0 -0
- data/lib/bsm/bsm_parse.rb +0 -18
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0f86f5bdcd9b98750e22cff1c56e95f64f45e4bd89daaedc11fccfffb5657bf4
|
|
4
|
+
data.tar.gz: 966a4663e01a1d395110583be23eec8a89936e9b6bbc13114cf516b98372728c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e0012606b0e0ec82f03fbe23cda9b73d856746def0863290b72e62bdb493414b3ceb3d652ca8c9979dcc3cf9aa080d60b02568039fdc40a0af04f72b684156d1
|
|
7
|
+
data.tar.gz: 4921666d0a125c739bcd5f13bc85190200f41feaa7de7712626312ce545465f0874d0703c94e24fff634ee498e3336b8a7f0e0b3499c2b2ddaaf402752ae185a
|
data/Gemfile
CHANGED
|
@@ -1,6 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
source 'https://rubygems.org'
|
|
2
4
|
|
|
3
5
|
# Specify your gem's dependencies in bsm.gemspec
|
|
4
6
|
gemspec
|
|
5
7
|
|
|
6
|
-
|
|
8
|
+
# Specify development dependencies here and not in the gemspec
|
|
9
|
+
gem 'markspec'
|
|
10
|
+
gem 'rake'
|
|
11
|
+
gem 'rake-compiler'
|
|
12
|
+
gem 'rspec'
|
|
13
|
+
gem 'rubocop'
|
|
14
|
+
gem 'rubocop-rake'
|
|
15
|
+
gem 'rubocop-rspec'
|
|
16
|
+
gem 'rubocop-yard'
|
|
17
|
+
gem 'yard'
|
|
18
|
+
gem 'yard-rspec'
|
data/bsm.gemspec
CHANGED
|
@@ -1,37 +1,38 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'lib/bsm/version'
|
|
4
4
|
|
|
5
5
|
Gem::Specification.new do |spec|
|
|
6
6
|
spec.name = 'bsm'
|
|
7
7
|
spec.version = Bsm::VERSION
|
|
8
8
|
spec.authors = ['David Siaw']
|
|
9
|
-
spec.email = ['
|
|
9
|
+
spec.email = ['874280+davidsiaw@users.noreply.github.com']
|
|
10
10
|
|
|
11
|
-
spec.summary = 'a
|
|
12
|
-
spec.description = '
|
|
13
|
-
|
|
11
|
+
spec.summary = 'Write binary files in a human-readable way'
|
|
12
|
+
spec.description = 'Bsm converts literate text (semicolon-prefixed lines of hex, ' \
|
|
13
|
+
'strings, bitfields) into binary. Backed by the native bsm2 library.'
|
|
14
|
+
spec.homepage = 'https://github.com/davidsiaw/bsm'
|
|
14
15
|
spec.license = 'MIT'
|
|
16
|
+
spec.required_ruby_version = Gem::Requirement.new('>= 3.0')
|
|
15
17
|
|
|
16
|
-
|
|
17
|
-
spec.metadata['allowed_push_host'] = 'https://rubygems.org'
|
|
18
|
+
spec.metadata['allowed_push_host'] = 'https://rubygems.org'
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
raise 'RubyGems 2.0 or newer is required to protect against ' \
|
|
24
|
-
'public gem pushes.'
|
|
25
|
-
end
|
|
20
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
|
21
|
+
spec.metadata['source_code_uri'] = 'https://github.com/davidsiaw/bsm'
|
|
22
|
+
spec.metadata['changelog_uri'] = 'https://github.com/davidsiaw/bsm'
|
|
23
|
+
spec.metadata['rubygems_mfa_required'] = 'true'
|
|
26
24
|
|
|
27
|
-
|
|
25
|
+
# bsm2 is vendored as a git submodule and built from source at install time
|
|
26
|
+
# by ext/bsm/internal/extconf.rb. Ship the source + build files so the gem
|
|
27
|
+
# is self-contained. Exclude ext build artifacts (mkmf output) from the gem.
|
|
28
|
+
spec.files = (Dir['{exe,lib,ext}/**/*'] +
|
|
29
|
+
Dir['bsm2/src/**/*'] +
|
|
30
|
+
%w[Gemfile bsm.gemspec bsm2/Makefile]).reject do |f|
|
|
31
|
+
f =~ %r{^ext/.*\.(o|so|bundle)$} ||
|
|
32
|
+
f =~ %r{^ext/.*/(Makefile|mkmf\.log)$}
|
|
33
|
+
end
|
|
28
34
|
spec.bindir = 'exe'
|
|
29
35
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
30
36
|
spec.require_paths = ['lib']
|
|
31
37
|
spec.extensions = %w[ext/bsm/internal/extconf.rb]
|
|
32
|
-
|
|
33
|
-
spec.add_development_dependency 'bundler', '~> 2.0'
|
|
34
|
-
spec.add_development_dependency 'rake', '~> 10.0'
|
|
35
|
-
spec.add_development_dependency 'rake-compiler'
|
|
36
|
-
spec.add_development_dependency 'rspec', '~> 3.0'
|
|
37
38
|
end
|
data/bsm2/Makefile
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
SRC_DIR := src
|
|
2
|
+
BSMSRC_DIR := src/bsm2
|
|
3
|
+
MSBSRC_DIR := src/msb2
|
|
4
|
+
OBJ_DIR := obj
|
|
5
|
+
BSMOBJ_DIR := obj/bsm2
|
|
6
|
+
MSBOBJ_DIR := obj/msb2
|
|
7
|
+
LIB_DIR := lib
|
|
8
|
+
BIN_DIR := bin
|
|
9
|
+
|
|
10
|
+
EXE := $(BIN_DIR)/bsm2
|
|
11
|
+
MSBEXE := $(BIN_DIR)/msb2
|
|
12
|
+
LIB := $(LIB_DIR)/libbsm2.a
|
|
13
|
+
CAPITEST := $(BIN_DIR)/c_api_test
|
|
14
|
+
EMPTYTEST := $(BIN_DIR)/empty_out_test
|
|
15
|
+
|
|
16
|
+
# Library sources (top-level src/*.cpp) — no main, no I/O
|
|
17
|
+
LIB_SRC := $(wildcard $(SRC_DIR)/*.cpp)
|
|
18
|
+
LIB_OBJ := $(patsubst $(SRC_DIR)/%.cpp, $(OBJ_DIR)/%.o, $(LIB_SRC))
|
|
19
|
+
|
|
20
|
+
# bsm2 executable main
|
|
21
|
+
BSMSRC := $(wildcard $(BSMSRC_DIR)/*.cpp)
|
|
22
|
+
BSMOBJ := $(patsubst $(BSMSRC_DIR)/%.cpp, $(BSMOBJ_DIR)/%.o, $(BSMSRC))
|
|
23
|
+
|
|
24
|
+
# msb2 executable (standalone, no lib dependency)
|
|
25
|
+
MSBSRC := $(wildcard $(MSBSRC_DIR)/*.cpp)
|
|
26
|
+
MSBOBJ := $(patsubst $(MSBSRC_DIR)/%.cpp, $(MSBOBJ_DIR)/%.o, $(MSBSRC))
|
|
27
|
+
|
|
28
|
+
CPPFLAGS := -Isrc -MMD -MP -std=c++17
|
|
29
|
+
CFLAGS := -Wall -Wpedantic -fPIC
|
|
30
|
+
LDFLAGS :=
|
|
31
|
+
LDLIBS :=
|
|
32
|
+
|
|
33
|
+
PREFIX ?= /usr/local
|
|
34
|
+
BINDIR ?= $(PREFIX)/bin
|
|
35
|
+
INSTALL ?= install
|
|
36
|
+
|
|
37
|
+
all: $(EXE) $(MSBEXE)
|
|
38
|
+
|
|
39
|
+
libbsm2: $(LIB)
|
|
40
|
+
|
|
41
|
+
$(LIB): $(LIB_OBJ) | $(LIB_DIR)
|
|
42
|
+
$(AR) rcs $@ $^
|
|
43
|
+
|
|
44
|
+
# bsm2: main.o linked against libbsm2
|
|
45
|
+
$(EXE): $(BSMOBJ) $(LIB) | $(BIN_DIR)
|
|
46
|
+
$(CXX) $(LDFLAGS) $(BSMOBJ) -L$(LIB_DIR) -lbsm2 $(LDLIBS) -o $@
|
|
47
|
+
|
|
48
|
+
# msb2: standalone, does not use libbsm2
|
|
49
|
+
$(MSBEXE): $(MSBOBJ) | $(BIN_DIR)
|
|
50
|
+
$(CXX) $(LDFLAGS) $^ $(LDLIBS) -o $@
|
|
51
|
+
|
|
52
|
+
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.cpp | $(OBJ_DIR)
|
|
53
|
+
$(CXX) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
|
|
54
|
+
|
|
55
|
+
$(BSMOBJ_DIR)/%.o: $(BSMSRC_DIR)/%.cpp | $(BSMOBJ_DIR)
|
|
56
|
+
$(CXX) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
|
|
57
|
+
|
|
58
|
+
$(MSBOBJ_DIR)/%.o: $(MSBSRC_DIR)/%.cpp | $(MSBOBJ_DIR)
|
|
59
|
+
$(CXX) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
|
|
60
|
+
|
|
61
|
+
$(BIN_DIR) $(OBJ_DIR) $(BSMOBJ_DIR) $(MSBOBJ_DIR) $(LIB_DIR):
|
|
62
|
+
mkdir -p $@
|
|
63
|
+
|
|
64
|
+
.PHONY: clean format test test-c-api memtest install uninstall libbsm2
|
|
65
|
+
|
|
66
|
+
install: all
|
|
67
|
+
$(INSTALL) -d $(DESTDIR)$(BINDIR)
|
|
68
|
+
$(INSTALL) -m 755 $(EXE) $(DESTDIR)$(BINDIR)/bsm2
|
|
69
|
+
$(INSTALL) -m 755 $(MSBEXE) $(DESTDIR)$(BINDIR)/msb2
|
|
70
|
+
|
|
71
|
+
uninstall:
|
|
72
|
+
$(RM) $(DESTDIR)$(BINDIR)/bsm2 $(DESTDIR)$(BINDIR)/msb2
|
|
73
|
+
|
|
74
|
+
# C tests: C sources linked against libbsm2 (g++ driver for libstdc++)
|
|
75
|
+
# Built with -g -O0 so valgrind reports are meaningful.
|
|
76
|
+
$(CAPITEST): test/c_api_test.c $(LIB) | $(BIN_DIR)
|
|
77
|
+
$(CC) -Isrc -g -O0 -MMD -MP -c $< -o $(OBJ_DIR)/c_api_test.o
|
|
78
|
+
$(CXX) $(LDFLAGS) $(OBJ_DIR)/c_api_test.o -L$(LIB_DIR) -lbsm2 $(LDLIBS) -o $@
|
|
79
|
+
|
|
80
|
+
$(EMPTYTEST): test/empty_out_test.c $(LIB) | $(BIN_DIR)
|
|
81
|
+
$(CC) -Isrc -g -O0 -MMD -MP -c $< -o $(OBJ_DIR)/empty_out_test.o
|
|
82
|
+
$(CXX) $(LDFLAGS) $(OBJ_DIR)/empty_out_test.o -L$(LIB_DIR) -lbsm2 $(LDLIBS) -o $@
|
|
83
|
+
|
|
84
|
+
test-c-api: $(CAPITEST) $(EMPTYTEST)
|
|
85
|
+
./$(CAPITEST)
|
|
86
|
+
./$(EMPTYTEST)
|
|
87
|
+
|
|
88
|
+
# Valgrind memory check of the C API tests. Requires valgrind on PATH.
|
|
89
|
+
VALGRIND := valgrind --leak-check=full --show-leak-kinds=all --error-exitcode=99
|
|
90
|
+
|
|
91
|
+
memtest: $(CAPITEST) $(EMPTYTEST)
|
|
92
|
+
$(VALGRIND) ./$(CAPITEST)
|
|
93
|
+
$(VALGRIND) ./$(EMPTYTEST)
|
|
94
|
+
|
|
95
|
+
test: $(EXE) test-c-api
|
|
96
|
+
PATH="$(HOME)/.local/share/mise/installs/ruby/3.3.5/bin:$$PATH" markspec
|
|
97
|
+
|
|
98
|
+
format:
|
|
99
|
+
uncrustify -c uncrustify.cfg src/*.cpp src/*.h src/bsm2/*.cpp src/msb2/*.cpp --replace --no-backup
|
|
100
|
+
|
|
101
|
+
clean:
|
|
102
|
+
@$(RM) -rv $(BIN_DIR) $(OBJ_DIR) $(LIB_DIR)
|
|
103
|
+
|
|
104
|
+
-include $(LIB_OBJ:.o=.d) $(BSMOBJ:.o=.d) $(MSBOBJ:.o=.d)
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#include "error_info.h"
|
|
2
|
+
#include "processor.h"
|
|
3
|
+
#include <iostream>
|
|
4
|
+
#include <memory>
|
|
5
|
+
#include <string>
|
|
6
|
+
|
|
7
|
+
static void report_error(ErrorInfo const &e)
|
|
8
|
+
{
|
|
9
|
+
std::cerr << "BSM: Error at line " << e.line_number << " and column "
|
|
10
|
+
<< e.column << std::endl
|
|
11
|
+
<< "---" << std::endl;
|
|
12
|
+
std::cerr << e.line_text << std::endl;
|
|
13
|
+
|
|
14
|
+
for (unsigned i = 0; i < e.column; i++)
|
|
15
|
+
{
|
|
16
|
+
std::cerr << " ";
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
std::cerr << "^";
|
|
20
|
+
|
|
21
|
+
for (unsigned i = 0; i < e.char_length; i++)
|
|
22
|
+
{
|
|
23
|
+
std::cerr << "~";
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
std::cerr << " " << e.message << std::endl;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
int main()
|
|
30
|
+
{
|
|
31
|
+
std::unique_ptr<Processor> p = std::make_unique<Processor>();
|
|
32
|
+
|
|
33
|
+
while (std::cin)
|
|
34
|
+
{
|
|
35
|
+
std::string input_line;
|
|
36
|
+
std::getline(std::cin, input_line);
|
|
37
|
+
|
|
38
|
+
ErrorInfo error = {};
|
|
39
|
+
std::string output = p->lex_line(input_line, &error);
|
|
40
|
+
std::cout << output;
|
|
41
|
+
|
|
42
|
+
if (error.line_number > 0)
|
|
43
|
+
{
|
|
44
|
+
report_error(error);
|
|
45
|
+
return EXIT_FAILURE;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return EXIT_SUCCESS;
|
|
50
|
+
}
|
data/bsm2/src/c_api.cpp
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
#include "c_api.h"
|
|
2
|
+
#include "error_info.h"
|
|
3
|
+
#include "processor.h"
|
|
4
|
+
|
|
5
|
+
#include <cstdlib>
|
|
6
|
+
#include <cstring>
|
|
7
|
+
#include <new>
|
|
8
|
+
#include <string>
|
|
9
|
+
|
|
10
|
+
struct bsm2_processor
|
|
11
|
+
{
|
|
12
|
+
Processor impl;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
struct bsm2_error
|
|
16
|
+
{
|
|
17
|
+
std::string message;
|
|
18
|
+
std::string line_text;
|
|
19
|
+
unsigned line_number;
|
|
20
|
+
unsigned column;
|
|
21
|
+
unsigned char_length;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
bsm2_processor* bsm2_new(void)
|
|
25
|
+
{
|
|
26
|
+
bsm2_processor* p =
|
|
27
|
+
static_cast<bsm2_processor*>(std::malloc(sizeof(bsm2_processor)));
|
|
28
|
+
if (!p)
|
|
29
|
+
{
|
|
30
|
+
return nullptr;
|
|
31
|
+
}
|
|
32
|
+
new (&p->impl) Processor();
|
|
33
|
+
return p;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
void bsm2_free(bsm2_processor* p)
|
|
37
|
+
{
|
|
38
|
+
if (!p)
|
|
39
|
+
{
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
p->impl.~Processor();
|
|
43
|
+
std::free(p);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
char* bsm2_convert_line(bsm2_processor* p, const char* line, size_t len,
|
|
47
|
+
size_t* out_len, bsm2_error** err)
|
|
48
|
+
{
|
|
49
|
+
if (err)
|
|
50
|
+
{
|
|
51
|
+
*err = nullptr;
|
|
52
|
+
}
|
|
53
|
+
if (!p || !line)
|
|
54
|
+
{
|
|
55
|
+
return nullptr;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
ErrorInfo info = {};
|
|
59
|
+
std::string input(line, len);
|
|
60
|
+
std::string out = p->impl.lex_line(input, &info);
|
|
61
|
+
|
|
62
|
+
if (info.line_number > 0)
|
|
63
|
+
{
|
|
64
|
+
if (err)
|
|
65
|
+
{
|
|
66
|
+
bsm2_error* e = new (std::nothrow) bsm2_error();
|
|
67
|
+
if (e)
|
|
68
|
+
{
|
|
69
|
+
e->message = info.message;
|
|
70
|
+
e->line_text = info.line_text;
|
|
71
|
+
e->line_number = info.line_number;
|
|
72
|
+
e->column = info.column;
|
|
73
|
+
e->char_length = info.char_length;
|
|
74
|
+
*err = e;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return nullptr;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
size_t n = out.size();
|
|
81
|
+
if (out_len)
|
|
82
|
+
{
|
|
83
|
+
*out_len = n;
|
|
84
|
+
}
|
|
85
|
+
// Always hand back a non-NULL buffer on success so callers can distinguish
|
|
86
|
+
// success-with-empty-output from error (NULL + *err set).
|
|
87
|
+
char* buf = static_cast<char*>(std::malloc(n ? n : 1));
|
|
88
|
+
if (!buf)
|
|
89
|
+
{
|
|
90
|
+
return nullptr;
|
|
91
|
+
}
|
|
92
|
+
if (n)
|
|
93
|
+
{
|
|
94
|
+
std::memcpy(buf, out.data(), n);
|
|
95
|
+
}
|
|
96
|
+
return buf;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const char* bsm2_error_message(bsm2_error* e)
|
|
100
|
+
{
|
|
101
|
+
return e ? e->message.c_str() : "";
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
unsigned bsm2_error_line(bsm2_error* e)
|
|
105
|
+
{
|
|
106
|
+
return e ? e->line_number : 0;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
unsigned bsm2_error_column(bsm2_error* e)
|
|
110
|
+
{
|
|
111
|
+
return e ? e->column : 0;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
unsigned bsm2_error_length(bsm2_error* e)
|
|
115
|
+
{
|
|
116
|
+
return e ? e->char_length : 0;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const char* bsm2_error_line_text(bsm2_error* e)
|
|
120
|
+
{
|
|
121
|
+
return e ? e->line_text.c_str() : "";
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
void bsm2_error_free(bsm2_error* e)
|
|
125
|
+
{
|
|
126
|
+
delete e;
|
|
127
|
+
}
|
data/bsm2/src/c_api.h
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#ifndef BSM2_C_API_H
|
|
2
|
+
#define BSM2_C_API_H
|
|
3
|
+
|
|
4
|
+
#include <stddef.h>
|
|
5
|
+
|
|
6
|
+
#ifdef __cplusplus
|
|
7
|
+
extern "C" {
|
|
8
|
+
#endif
|
|
9
|
+
|
|
10
|
+
/* Opaque handle: one conversion session. The internal line counter
|
|
11
|
+
* accumulates across calls, so feed lines in order. */
|
|
12
|
+
typedef struct bsm2_processor bsm2_processor;
|
|
13
|
+
|
|
14
|
+
/* Opaque error. Heap-allocated by bsm2_convert_line on failure; caller frees
|
|
15
|
+
* with bsm2_error_free(). Pure data — the caller owns presentation. */
|
|
16
|
+
typedef struct bsm2_error bsm2_error;
|
|
17
|
+
|
|
18
|
+
bsm2_processor* bsm2_new(void);
|
|
19
|
+
void bsm2_free(bsm2_processor* p);
|
|
20
|
+
|
|
21
|
+
/* Feed one raw line, including its leading ';' (the ';' convention lives in
|
|
22
|
+
* the library; non-';' lines are ignored and yield empty output).
|
|
23
|
+
*
|
|
24
|
+
* On success: returns a malloc'd buffer of *out_len bytes (caller frees with
|
|
25
|
+
* free(); always non-NULL even when *out_len == 0). *err is set to NULL.
|
|
26
|
+
* On error: returns NULL and sets *err (caller frees with bsm2_error_free()).*/
|
|
27
|
+
char* bsm2_convert_line(bsm2_processor* p, const char* line, size_t len,
|
|
28
|
+
size_t* out_len, bsm2_error** err);
|
|
29
|
+
|
|
30
|
+
const char* bsm2_error_message(bsm2_error* e);
|
|
31
|
+
unsigned bsm2_error_line(bsm2_error* e);
|
|
32
|
+
unsigned bsm2_error_column(bsm2_error* e);
|
|
33
|
+
unsigned bsm2_error_length(bsm2_error* e);
|
|
34
|
+
const char* bsm2_error_line_text(bsm2_error* e);
|
|
35
|
+
void bsm2_error_free(bsm2_error* e);
|
|
36
|
+
|
|
37
|
+
#ifdef __cplusplus
|
|
38
|
+
}
|
|
39
|
+
#endif
|
|
40
|
+
|
|
41
|
+
#endif /* BSM2_C_API_H */
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
#ifndef CONVERTER_H
|
|
2
|
+
#define CONVERTER_H
|
|
3
|
+
|
|
4
|
+
#include <string>
|
|
5
|
+
#include <sstream>
|
|
6
|
+
|
|
7
|
+
class Converter
|
|
8
|
+
{
|
|
9
|
+
public:
|
|
10
|
+
Converter()
|
|
11
|
+
{
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
~Converter()
|
|
15
|
+
{
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
char convert_hexpair(const std::string& yytext)
|
|
19
|
+
{
|
|
20
|
+
int chr = stoi(std::string(yytext), nullptr, 16);
|
|
21
|
+
return static_cast<char>(chr);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
char convert_quotechar(const std::string& yytext)
|
|
25
|
+
{
|
|
26
|
+
return yytext[1];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
std::string convert_doublequote(const std::string& yytext)
|
|
30
|
+
{
|
|
31
|
+
std::stringstream result;
|
|
32
|
+
|
|
33
|
+
for (uint i = 1; i < yytext.length() - 1; i++)
|
|
34
|
+
{
|
|
35
|
+
result << yytext[i];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return result.str();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
unsigned char convert_bindigits(const std::string& yytext)
|
|
42
|
+
{
|
|
43
|
+
unsigned char c = 0;
|
|
44
|
+
|
|
45
|
+
for (uint i = 1; i < yytext.length() - 1; i++)
|
|
46
|
+
{
|
|
47
|
+
if (yytext[i] == '1')
|
|
48
|
+
{
|
|
49
|
+
c += 1 << (8-i);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return c;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
#endif // CONVERTER_H
|
data/bsm2/src/lexer.cpp
ADDED
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
#include "lexer.h"
|
|
2
|
+
#include <cctype>
|
|
3
|
+
#include <string>
|
|
4
|
+
|
|
5
|
+
static char to_char(int c)
|
|
6
|
+
{
|
|
7
|
+
return static_cast<char>(c);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
static char hex_to_byte(char hi, char lo)
|
|
11
|
+
{
|
|
12
|
+
auto nibble = [](char c) -> int
|
|
13
|
+
{
|
|
14
|
+
if (c >= '0' && c <= '9')
|
|
15
|
+
return c - '0';
|
|
16
|
+
|
|
17
|
+
if (c >= 'a' && c <= 'f')
|
|
18
|
+
return c - 'a' + 10;
|
|
19
|
+
|
|
20
|
+
if (c >= 'A' && c <= 'F')
|
|
21
|
+
return c - 'A' + 10;
|
|
22
|
+
|
|
23
|
+
return -1;
|
|
24
|
+
};
|
|
25
|
+
int hi_val = nibble(hi);
|
|
26
|
+
int lo_val = nibble(lo);
|
|
27
|
+
|
|
28
|
+
if (hi_val < 0 || lo_val < 0)
|
|
29
|
+
return to_char(-1);
|
|
30
|
+
|
|
31
|
+
return to_char((hi_val << 4) | lo_val);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
static char read_single_char(std::string const &token)
|
|
35
|
+
{
|
|
36
|
+
return to_char(token[1]);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
static std::string read_double_string(std::string const &token)
|
|
40
|
+
{
|
|
41
|
+
return token.substr(1, token.size() - 2);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
static bool is_whitespace(char c)
|
|
45
|
+
{
|
|
46
|
+
return c == ' ' || c == '\t' || c == '\r' || c == '\n';
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
static char read_bitfield(std::string const &token)
|
|
50
|
+
{
|
|
51
|
+
unsigned char value = 0;
|
|
52
|
+
|
|
53
|
+
for (size_t i = 1; i <= 8; i++)
|
|
54
|
+
{
|
|
55
|
+
if (token[i] == '1')
|
|
56
|
+
{
|
|
57
|
+
value |= (1u << (8 - i));
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return to_char(value);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
Lexer::Lexer(std::string const &input, unsigned line_number,
|
|
65
|
+
std::string const &line_text)
|
|
66
|
+
: input(input), m_pos(0), m_column(1), m_line_number(line_number),
|
|
67
|
+
|
|
68
|
+
m_line_text(line_text)
|
|
69
|
+
{
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
size_t Lexer::pos() const
|
|
73
|
+
{
|
|
74
|
+
return m_pos;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
size_t Lexer::size() const
|
|
78
|
+
{
|
|
79
|
+
return input.size();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
bool Lexer::consume()
|
|
83
|
+
{
|
|
84
|
+
if (m_pos >= input.size())
|
|
85
|
+
{
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
m_error_line = m_line_number;
|
|
90
|
+
m_error_line_text = m_line_text;
|
|
91
|
+
char c = current_char();
|
|
92
|
+
return dispatch(c, m_column);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
unsigned Lexer::error_line_number() const
|
|
96
|
+
{
|
|
97
|
+
return m_error_line;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
unsigned Lexer::error_column() const
|
|
101
|
+
{
|
|
102
|
+
return m_error_column;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
unsigned Lexer::error_length() const
|
|
106
|
+
{
|
|
107
|
+
return m_error_length;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
std::string const &Lexer::error_line_text() const
|
|
111
|
+
{
|
|
112
|
+
return m_error_line_text;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
std::string const &Lexer::error_message() const
|
|
116
|
+
{
|
|
117
|
+
return m_error_message;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
bool Lexer::fail(unsigned column, unsigned length, std::string const &message)
|
|
121
|
+
{
|
|
122
|
+
m_error_column = column;
|
|
123
|
+
m_error_length = length;
|
|
124
|
+
m_error_message = message;
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
std::string const &Lexer::result() const
|
|
129
|
+
{
|
|
130
|
+
return m_result;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
char Lexer::current_char() const
|
|
134
|
+
{
|
|
135
|
+
return input[m_pos];
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
bool Lexer::dispatch(char c, unsigned error_column)
|
|
139
|
+
{
|
|
140
|
+
if (is_whitespace(c))
|
|
141
|
+
|
|
142
|
+
{
|
|
143
|
+
skip_whitespace();
|
|
144
|
+
return true;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (std::isxdigit(c))
|
|
148
|
+
|
|
149
|
+
{
|
|
150
|
+
return consume_hex_pair(error_column);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (c == '\'')
|
|
154
|
+
{
|
|
155
|
+
return consume_single_char(error_column);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (c == '"')
|
|
159
|
+
{
|
|
160
|
+
return consume_double_string(error_column);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (c == '<')
|
|
164
|
+
{
|
|
165
|
+
return consume_bitfield(error_column);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return fail(error_column, 1, "unrecognized character");
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
void Lexer::skip_whitespace()
|
|
172
|
+
{
|
|
173
|
+
m_pos++;
|
|
174
|
+
m_column++;
|
|
175
|
+
|
|
176
|
+
while (m_pos < input.size())
|
|
177
|
+
{
|
|
178
|
+
char c = input[m_pos];
|
|
179
|
+
|
|
180
|
+
if (is_whitespace(c))
|
|
181
|
+
|
|
182
|
+
{
|
|
183
|
+
m_pos++;
|
|
184
|
+
m_column++;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
else
|
|
188
|
+
{
|
|
189
|
+
break;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
bool Lexer::consume_hex_pair(unsigned error_column)
|
|
195
|
+
{
|
|
196
|
+
if (m_pos + 1 < input.size() && std::isxdigit(input[m_pos + 1]))
|
|
197
|
+
{
|
|
198
|
+
char first = input[m_pos];
|
|
199
|
+
char second = input[m_pos + 1];
|
|
200
|
+
m_pos += 2;
|
|
201
|
+
m_column += 2;
|
|
202
|
+
m_result += hex_to_byte(first, second);
|
|
203
|
+
return true;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
else
|
|
207
|
+
{
|
|
208
|
+
return fail(error_column, 1, "hex byte needs two hex digits");
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
bool Lexer::consume_single_char(unsigned error_column)
|
|
213
|
+
{
|
|
214
|
+
if (m_pos + 2 < input.size() && input[m_pos + 2] == '\'')
|
|
215
|
+
{
|
|
216
|
+
size_t token_start = m_pos;
|
|
217
|
+
m_pos += 3;
|
|
218
|
+
m_column += 3;
|
|
219
|
+
std::string token(input, token_start, 3);
|
|
220
|
+
|
|
221
|
+
m_result += read_single_char(token);
|
|
222
|
+
return true;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
else
|
|
226
|
+
{
|
|
227
|
+
return fail(error_column, 1,
|
|
228
|
+
"single quotes must hold exactly one character");
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
bool Lexer::consume_double_string(unsigned error_column)
|
|
233
|
+
{
|
|
234
|
+
size_t token_start = m_pos;
|
|
235
|
+
size_t close = input.find('"', m_pos + 1);
|
|
236
|
+
|
|
237
|
+
if (close == std::string::npos)
|
|
238
|
+
{
|
|
239
|
+
return fail(error_column, 1,
|
|
240
|
+
"string has no closing quote on this line");
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
m_pos = close + 1;
|
|
244
|
+
m_column += close - token_start + 1;
|
|
245
|
+
std::string token(input, token_start, close - token_start + 1);
|
|
246
|
+
|
|
247
|
+
m_result += read_double_string(token);
|
|
248
|
+
return true;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
bool Lexer::consume_bitfield(unsigned error_column)
|
|
252
|
+
{
|
|
253
|
+
if (m_pos + 9 >= input.size() || input[m_pos + 9] != '>')
|
|
254
|
+
{
|
|
255
|
+
return fail(error_column, 1,
|
|
256
|
+
"bitfield must be exactly 8 bits closed by '>' on the same line");
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
for (size_t i = 0; i < 8; i++)
|
|
260
|
+
{
|
|
261
|
+
char b = input[m_pos + 1 + i];
|
|
262
|
+
|
|
263
|
+
if (b != '0' && b != '1' && b != '.')
|
|
264
|
+
{
|
|
265
|
+
return fail(error_column + 1 + i, 1, "bitfield bits must be 0, 1, or .");
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
size_t token_start = m_pos;
|
|
270
|
+
m_pos += 10;
|
|
271
|
+
m_column += 10;
|
|
272
|
+
std::string token(input, token_start, 10);
|
|
273
|
+
|
|
274
|
+
m_result += read_bitfield(token);
|
|
275
|
+
return true;
|
|
276
|
+
}
|
data/bsm2/src/lexer.h
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
#ifndef LEXER_H
|
|
2
|
+
#define LEXER_H
|
|
3
|
+
|
|
4
|
+
#include "error_info.h"
|
|
5
|
+
#include <string>
|
|
6
|
+
|
|
7
|
+
class Lexer
|
|
8
|
+
{
|
|
9
|
+
std::string const &input;
|
|
10
|
+
size_t m_pos;
|
|
11
|
+
unsigned m_column;
|
|
12
|
+
unsigned m_error_line;
|
|
13
|
+
unsigned m_error_column;
|
|
14
|
+
unsigned m_error_length;
|
|
15
|
+
std::string m_error_line_text;
|
|
16
|
+
std::string m_error_message;
|
|
17
|
+
std::string m_result;
|
|
18
|
+
unsigned m_line_number;
|
|
19
|
+
std::string m_line_text;
|
|
20
|
+
|
|
21
|
+
public:
|
|
22
|
+
Lexer(std::string const &input, unsigned line_number,
|
|
23
|
+
std::string const &line_text);
|
|
24
|
+
|
|
25
|
+
size_t pos() const;
|
|
26
|
+
|
|
27
|
+
size_t size() const;
|
|
28
|
+
|
|
29
|
+
bool consume();
|
|
30
|
+
|
|
31
|
+
unsigned error_line_number() const;
|
|
32
|
+
|
|
33
|
+
unsigned error_column() const;
|
|
34
|
+
|
|
35
|
+
unsigned error_length() const;
|
|
36
|
+
|
|
37
|
+
std::string const &error_line_text() const;
|
|
38
|
+
std::string const &error_message() const;
|
|
39
|
+
std::string const &result() const;
|
|
40
|
+
|
|
41
|
+
private:
|
|
42
|
+
char current_char() const;
|
|
43
|
+
|
|
44
|
+
bool fail(unsigned column, unsigned length, std::string const &message);
|
|
45
|
+
|
|
46
|
+
bool dispatch(char c, unsigned error_column);
|
|
47
|
+
|
|
48
|
+
void skip_whitespace();
|
|
49
|
+
|
|
50
|
+
bool consume_hex_pair(unsigned error_column);
|
|
51
|
+
|
|
52
|
+
bool consume_single_char(unsigned error_column);
|
|
53
|
+
|
|
54
|
+
bool consume_double_string(unsigned error_column);
|
|
55
|
+
|
|
56
|
+
bool consume_bitfield(unsigned error_column);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
#endif // LEXER_H
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#include <iostream>
|
|
2
|
+
#include <iomanip>
|
|
3
|
+
#include <fstream>
|
|
4
|
+
|
|
5
|
+
int main()
|
|
6
|
+
{
|
|
7
|
+
int bytecount = 0;
|
|
8
|
+
|
|
9
|
+
std::cout << ";";
|
|
10
|
+
|
|
11
|
+
do
|
|
12
|
+
{
|
|
13
|
+
unsigned char c = std::cin.get();
|
|
14
|
+
|
|
15
|
+
if (!std::cin.good())
|
|
16
|
+
{
|
|
17
|
+
break;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
std::cout << " " << std::setw(2) << std::setfill('0') << std::hex << (int)c;
|
|
21
|
+
bytecount++;
|
|
22
|
+
|
|
23
|
+
if (bytecount % 16 == 0)
|
|
24
|
+
{
|
|
25
|
+
std::cout << "\n;";
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
else if (bytecount % 4 == 0)
|
|
29
|
+
{
|
|
30
|
+
std::cout << " ";
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
while (std::cin.good());
|
|
34
|
+
|
|
35
|
+
std::cout << "\nTotal bytes: " << bytecount << std::endl;
|
|
36
|
+
|
|
37
|
+
return EXIT_SUCCESS;
|
|
38
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#include "processor.h"
|
|
2
|
+
#include "lexer.h"
|
|
3
|
+
#include <string>
|
|
4
|
+
|
|
5
|
+
std::string Processor::lex_line(std::string const &input, ErrorInfo *out_error)
|
|
6
|
+
{
|
|
7
|
+
curline++;
|
|
8
|
+
|
|
9
|
+
if (input.length() == 0 || input[0] != ';')
|
|
10
|
+
{
|
|
11
|
+
return "";
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
std::string const rest = input.substr(1);
|
|
15
|
+
Lexer l(rest, curline, input);
|
|
16
|
+
|
|
17
|
+
while (l.pos() < l.size())
|
|
18
|
+
{
|
|
19
|
+
if (!l.consume())
|
|
20
|
+
{
|
|
21
|
+
out_error->line_number = l.error_line_number();
|
|
22
|
+
out_error->column = l.error_column();
|
|
23
|
+
out_error->char_length = l.error_length();
|
|
24
|
+
out_error->line_text = l.error_line_text();
|
|
25
|
+
out_error->message = l.error_message();
|
|
26
|
+
return "";
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return l.result();
|
|
31
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#ifndef PROCESSOR_H
|
|
2
|
+
#define PROCESSOR_H
|
|
3
|
+
|
|
4
|
+
#include "lexer.h"
|
|
5
|
+
#include <string>
|
|
6
|
+
|
|
7
|
+
class Processor
|
|
8
|
+
{
|
|
9
|
+
unsigned curline;
|
|
10
|
+
|
|
11
|
+
public:
|
|
12
|
+
Processor() : curline(0)
|
|
13
|
+
{
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
~Processor()
|
|
17
|
+
{
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
std::string lex_line(std::string const &input, ErrorInfo *out_error);
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
#endif // PROCESSOR_H
|
data/exe/bsm
CHANGED
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
2
3
|
|
|
3
|
-
require 'bsm/bsm_parse'
|
|
4
4
|
require 'json'
|
|
5
|
+
require 'bsm'
|
|
5
6
|
|
|
6
|
-
|
|
7
|
+
content = ARGF.to_a.join("\n")
|
|
8
|
+
gen = Bsm::Generator.new
|
|
7
9
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
ast['hexlits'].each do |chr|
|
|
11
|
-
result << chr['_token'].to_i(16)
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
print result
|
|
10
|
+
print gen.generate(content)
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
#include "c_api.h"
|
|
2
|
+
|
|
3
|
+
#include <ruby.h>
|
|
4
|
+
#include <ruby/encoding.h>
|
|
5
|
+
#include <string.h>
|
|
6
|
+
|
|
7
|
+
static VALUE mBsm;
|
|
8
|
+
static VALUE cGenerator;
|
|
9
|
+
static VALUE eInvalidInput;
|
|
10
|
+
|
|
11
|
+
struct generator_wrapper
|
|
12
|
+
{
|
|
13
|
+
bsm2_processor* proc;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
static void generator_free(void* ptr);
|
|
17
|
+
|
|
18
|
+
static const rb_data_type_t generator_data_type = {
|
|
19
|
+
"Bsm/GeneratorInternal",
|
|
20
|
+
{NULL, generator_free, NULL},
|
|
21
|
+
NULL,
|
|
22
|
+
NULL,
|
|
23
|
+
RUBY_TYPED_FREE_IMMEDIATELY,
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
static void generator_free(void* ptr)
|
|
27
|
+
{
|
|
28
|
+
struct generator_wrapper* w = (struct generator_wrapper*)ptr;
|
|
29
|
+
if (w)
|
|
30
|
+
{
|
|
31
|
+
if (w->proc)
|
|
32
|
+
{
|
|
33
|
+
bsm2_free(w->proc);
|
|
34
|
+
}
|
|
35
|
+
xfree(w);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
static VALUE generator_alloc(VALUE klass)
|
|
40
|
+
{
|
|
41
|
+
struct generator_wrapper* w;
|
|
42
|
+
VALUE obj = TypedData_Make_Struct(
|
|
43
|
+
klass, struct generator_wrapper, &(generator_data_type), w);
|
|
44
|
+
w->proc = bsm2_new();
|
|
45
|
+
if (!w->proc)
|
|
46
|
+
{
|
|
47
|
+
rb_raise(eInvalidInput, "bsm2_new failed");
|
|
48
|
+
}
|
|
49
|
+
return obj;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
static VALUE generator_generate_raw(VALUE self, VALUE input)
|
|
53
|
+
{
|
|
54
|
+
struct generator_wrapper* w;
|
|
55
|
+
TypedData_Get_Struct(self, struct generator_wrapper, &generator_data_type, w);
|
|
56
|
+
|
|
57
|
+
input = rb_String(input);
|
|
58
|
+
|
|
59
|
+
/* Iterate over lines, feeding each to bsm2_convert_line, concatenating
|
|
60
|
+
* the returned bytes into one binary String. */
|
|
61
|
+
VALUE result = rb_str_new_literal("");
|
|
62
|
+
rb_enc_associate(result, rb_ascii8bit_encoding());
|
|
63
|
+
|
|
64
|
+
char* cstr = StringValuePtr(input);
|
|
65
|
+
long total = RSTRING_LEN(input);
|
|
66
|
+
long line_start = 0;
|
|
67
|
+
|
|
68
|
+
for (long i = 0; i <= total; i++)
|
|
69
|
+
{
|
|
70
|
+
if (i == total || cstr[i] == '\n')
|
|
71
|
+
{
|
|
72
|
+
long line_len = i - line_start;
|
|
73
|
+
const char* line = cstr + line_start;
|
|
74
|
+
|
|
75
|
+
size_t out_len = 0;
|
|
76
|
+
bsm2_error* err = NULL;
|
|
77
|
+
char* out = bsm2_convert_line(w->proc, line, (size_t)line_len,
|
|
78
|
+
&out_len, &err);
|
|
79
|
+
if (!out)
|
|
80
|
+
{
|
|
81
|
+
if (err)
|
|
82
|
+
{
|
|
83
|
+
VALUE msg = rb_str_new_cstr(bsm2_error_message(err));
|
|
84
|
+
VALUE exc = rb_funcall(eInvalidInput, rb_intern("new"), 1, msg);
|
|
85
|
+
rb_iv_set(exc, "@line", UINT2NUM(bsm2_error_line(err)));
|
|
86
|
+
rb_iv_set(exc, "@column", UINT2NUM(bsm2_error_column(err)));
|
|
87
|
+
rb_iv_set(exc, "@length", UINT2NUM(bsm2_error_length(err)));
|
|
88
|
+
rb_iv_set(exc, "@line_text",
|
|
89
|
+
rb_str_new_cstr(bsm2_error_line_text(err)));
|
|
90
|
+
bsm2_error_free(err);
|
|
91
|
+
rb_exc_raise(exc);
|
|
92
|
+
}
|
|
93
|
+
rb_raise(eInvalidInput, "bsm2_convert_line failed");
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (out_len)
|
|
97
|
+
{
|
|
98
|
+
rb_str_cat(result, out, (long)out_len);
|
|
99
|
+
}
|
|
100
|
+
free(out);
|
|
101
|
+
|
|
102
|
+
line_start = i + 1;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return result;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
void Init_internal(void)
|
|
110
|
+
{
|
|
111
|
+
mBsm = rb_define_module("Bsm");
|
|
112
|
+
eInvalidInput =
|
|
113
|
+
rb_define_class_under(mBsm, "InvalidInput", rb_eStandardError);
|
|
114
|
+
cGenerator =
|
|
115
|
+
rb_define_class_under(mBsm, "GeneratorInternal", rb_cObject);
|
|
116
|
+
|
|
117
|
+
rb_define_alloc_func(cGenerator, generator_alloc);
|
|
118
|
+
rb_define_method(cGenerator, "generate_raw", generator_generate_raw, 1);
|
|
119
|
+
}
|
data/ext/bsm/internal/extconf.rb
CHANGED
|
@@ -2,6 +2,29 @@
|
|
|
2
2
|
|
|
3
3
|
require 'mkmf'
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
# bsm2 source is vendored at GEM_ROOT/bsm2 (a git submodule).
|
|
6
|
+
# extconf lives at GEM_ROOT/ext/bsm/internal, so three dirs up to the gem root.
|
|
7
|
+
BSM2_DIR = File.expand_path('../../../bsm2', __dir__)
|
|
8
|
+
|
|
9
|
+
unless File.directory?(File.join(BSM2_DIR, 'src'))
|
|
10
|
+
abort "bsm2 source not found at #{BSM2_DIR}/src. " \
|
|
11
|
+
'If this is a checkout, init the submodule: git submodule update --init'
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Build the static library from vendored source.
|
|
15
|
+
Dir.chdir(BSM2_DIR) do
|
|
16
|
+
success = system('make', 'libbsm2')
|
|
17
|
+
abort 'Failed to build bsm2/libbsm2.a (requires g++ with C++17)' unless success
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Expose the C API header and the built static lib to the extension.
|
|
21
|
+
# rubocop:disable Style/GlobalVars
|
|
22
|
+
$INCFLAGS << " -I#{File.join(BSM2_DIR, 'src')}"
|
|
23
|
+
$LDFLAGS << " -L#{File.join(BSM2_DIR, 'lib')}"
|
|
24
|
+
# rubocop:enable Style/GlobalVars
|
|
25
|
+
|
|
26
|
+
# libbsm2.a is C++ internally (std::string, new/delete); link the C++ runtime.
|
|
27
|
+
have_library('stdc++') or abort 'libstdc++ not found'
|
|
28
|
+
have_library('bsm2', 'bsm2_new') or abort 'libbsm2 not found / bsm2_new missing'
|
|
29
|
+
|
|
7
30
|
create_makefile('bsm/internal')
|
data/lib/bsm/internal.so
ADDED
|
Binary file
|
data/lib/bsm/version.rb
CHANGED
data/lib/bsm.rb
CHANGED
|
@@ -1,6 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: false
|
|
2
|
+
|
|
1
3
|
require 'bsm/version'
|
|
4
|
+
require 'bsm/internal'
|
|
2
5
|
|
|
3
6
|
module Bsm
|
|
4
|
-
|
|
5
|
-
#
|
|
7
|
+
# Raised when bsm2 rejects input. Carries the structured error fields
|
|
8
|
+
# (line, column, length, line_text) from the native library so callers can
|
|
9
|
+
# report precise locations.
|
|
10
|
+
class InvalidInput < StandardError
|
|
11
|
+
attr_accessor :line, :column, :length, :line_text
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# The generator class. Input is in bsm2 literate format: a line is data
|
|
15
|
+
# only if it starts with a semicolon (;); other lines are ignored.
|
|
16
|
+
class Generator
|
|
17
|
+
def initialize(_options = {})
|
|
18
|
+
@generator = GeneratorInternal.new
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def generate(input)
|
|
22
|
+
@generator.generate_raw(input)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
6
25
|
end
|
metadata
CHANGED
|
@@ -1,74 +1,19 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: bsm
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1
|
|
4
|
+
version: 0.2.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- David Siaw
|
|
8
|
-
autorequire:
|
|
8
|
+
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
12
|
-
dependencies:
|
|
13
|
-
-
|
|
14
|
-
|
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
|
16
|
-
requirements:
|
|
17
|
-
- - "~>"
|
|
18
|
-
- !ruby/object:Gem::Version
|
|
19
|
-
version: '2.0'
|
|
20
|
-
type: :development
|
|
21
|
-
prerelease: false
|
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
-
requirements:
|
|
24
|
-
- - "~>"
|
|
25
|
-
- !ruby/object:Gem::Version
|
|
26
|
-
version: '2.0'
|
|
27
|
-
- !ruby/object:Gem::Dependency
|
|
28
|
-
name: rake
|
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
|
30
|
-
requirements:
|
|
31
|
-
- - "~>"
|
|
32
|
-
- !ruby/object:Gem::Version
|
|
33
|
-
version: '10.0'
|
|
34
|
-
type: :development
|
|
35
|
-
prerelease: false
|
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
-
requirements:
|
|
38
|
-
- - "~>"
|
|
39
|
-
- !ruby/object:Gem::Version
|
|
40
|
-
version: '10.0'
|
|
41
|
-
- !ruby/object:Gem::Dependency
|
|
42
|
-
name: rake-compiler
|
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
|
44
|
-
requirements:
|
|
45
|
-
- - ">="
|
|
46
|
-
- !ruby/object:Gem::Version
|
|
47
|
-
version: '0'
|
|
48
|
-
type: :development
|
|
49
|
-
prerelease: false
|
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
-
requirements:
|
|
52
|
-
- - ">="
|
|
53
|
-
- !ruby/object:Gem::Version
|
|
54
|
-
version: '0'
|
|
55
|
-
- !ruby/object:Gem::Dependency
|
|
56
|
-
name: rspec
|
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
|
58
|
-
requirements:
|
|
59
|
-
- - "~>"
|
|
60
|
-
- !ruby/object:Gem::Version
|
|
61
|
-
version: '3.0'
|
|
62
|
-
type: :development
|
|
63
|
-
prerelease: false
|
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
-
requirements:
|
|
66
|
-
- - "~>"
|
|
67
|
-
- !ruby/object:Gem::Version
|
|
68
|
-
version: '3.0'
|
|
69
|
-
description: a longer description
|
|
11
|
+
date: 2026-07-21 00:00:00.000000000 Z
|
|
12
|
+
dependencies: []
|
|
13
|
+
description: Bsm converts literate text (semicolon-prefixed lines of hex, strings,
|
|
14
|
+
bitfields) into binary. Backed by the native bsm2 library.
|
|
70
15
|
email:
|
|
71
|
-
-
|
|
16
|
+
- 874280+davidsiaw@users.noreply.github.com
|
|
72
17
|
executables:
|
|
73
18
|
- bsm
|
|
74
19
|
extensions:
|
|
@@ -77,21 +22,34 @@ extra_rdoc_files: []
|
|
|
77
22
|
files:
|
|
78
23
|
- Gemfile
|
|
79
24
|
- bsm.gemspec
|
|
25
|
+
- bsm2/Makefile
|
|
26
|
+
- bsm2/src/bsm2/main.cpp
|
|
27
|
+
- bsm2/src/c_api.cpp
|
|
28
|
+
- bsm2/src/c_api.h
|
|
29
|
+
- bsm2/src/converter.h
|
|
30
|
+
- bsm2/src/error_info.h
|
|
31
|
+
- bsm2/src/lexer.cpp
|
|
32
|
+
- bsm2/src/lexer.h
|
|
33
|
+
- bsm2/src/msb2/README.md
|
|
34
|
+
- bsm2/src/msb2/main.cpp
|
|
35
|
+
- bsm2/src/processor.cpp
|
|
36
|
+
- bsm2/src/processor.h
|
|
80
37
|
- exe/bsm
|
|
38
|
+
- ext/bsm/internal/bsm.c
|
|
81
39
|
- ext/bsm/internal/extconf.rb
|
|
82
40
|
- lib/bsm.rb
|
|
83
|
-
- lib/bsm/
|
|
84
|
-
- lib/bsm/bsm_parse/internal.bundle
|
|
41
|
+
- lib/bsm/internal.so
|
|
85
42
|
- lib/bsm/version.rb
|
|
86
|
-
homepage: https://github.
|
|
43
|
+
homepage: https://github.com/davidsiaw/bsm
|
|
87
44
|
licenses:
|
|
88
45
|
- MIT
|
|
89
46
|
metadata:
|
|
90
47
|
allowed_push_host: https://rubygems.org
|
|
91
|
-
homepage_uri: https://github.
|
|
92
|
-
source_code_uri: https://github.
|
|
93
|
-
changelog_uri: https://github.
|
|
94
|
-
|
|
48
|
+
homepage_uri: https://github.com/davidsiaw/bsm
|
|
49
|
+
source_code_uri: https://github.com/davidsiaw/bsm
|
|
50
|
+
changelog_uri: https://github.com/davidsiaw/bsm
|
|
51
|
+
rubygems_mfa_required: 'true'
|
|
52
|
+
post_install_message:
|
|
95
53
|
rdoc_options: []
|
|
96
54
|
require_paths:
|
|
97
55
|
- lib
|
|
@@ -99,16 +57,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
99
57
|
requirements:
|
|
100
58
|
- - ">="
|
|
101
59
|
- !ruby/object:Gem::Version
|
|
102
|
-
version: '0'
|
|
60
|
+
version: '3.0'
|
|
103
61
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
104
62
|
requirements:
|
|
105
63
|
- - ">="
|
|
106
64
|
- !ruby/object:Gem::Version
|
|
107
65
|
version: '0'
|
|
108
66
|
requirements: []
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
signing_key:
|
|
67
|
+
rubygems_version: 3.5.22
|
|
68
|
+
signing_key:
|
|
112
69
|
specification_version: 4
|
|
113
|
-
summary: a
|
|
70
|
+
summary: Write binary files in a human-readable way
|
|
114
71
|
test_files: []
|
|
Binary file
|
data/lib/bsm/bsm_parse.rb
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'bsm/bsm_parse/internal'
|
|
4
|
-
|
|
5
|
-
module Bsm
|
|
6
|
-
# Parser module, wraps the native functions with some error handling
|
|
7
|
-
module BsmParse
|
|
8
|
-
def self.runtest(hello)
|
|
9
|
-
bsm_parse_test(hello)
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
def self.compile_to_ast(string)
|
|
13
|
-
return bsm_parse_compile_to_ast(string) if string.is_a? String
|
|
14
|
-
|
|
15
|
-
raise 'Invalid type given. This method only accepts strings'
|
|
16
|
-
end
|
|
17
|
-
end
|
|
18
|
-
end
|