base-lang 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +124 -0
- data/bin/base +40 -0
- data/lib/base/compiler.rb +150 -0
- data/lib/base/debugger.rb +87 -0
- data/lib/base/stack.rb +25 -0
- data/lib/base/vm.rb +122 -0
- data/lib/base.rb +4 -0
- metadata +50 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 352641028b34da47dec624f8cb3e135e6c336e47f854931649104b0c9a29629c
|
4
|
+
data.tar.gz: 490c1fa4fd25ec278bf2dd12e67f0cecc1b53da7a3eee848daee81121ddbff4e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b7d1e64864df4c093c56c70c2d212013c926082e213f8aad86ead4120e2f3cb0da5df3153402fa0a70da5716723a71163a1172a23a6a577cee4e23e5337725ed
|
7
|
+
data.tar.gz: 054abcc979edead6e60520bba2926a7614899fe73e349f81418ac87b617829de6d196b98c85b7f8156fb27b727a3fc45c0641440a5d8cdbc1667b133be145d54
|
data/README.md
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
# Base
|
2
|
+
|
3
|
+
A simple stack-based assembly programming language and VM, made for learning.
|
4
|
+
|
5
|
+
## Using it
|
6
|
+
|
7
|
+
Enter a simple program:
|
8
|
+
|
9
|
+
# alphabet.base
|
10
|
+
|
11
|
+
.main
|
12
|
+
push "A"
|
13
|
+
|
14
|
+
.loop
|
15
|
+
# Print letter to the screen
|
16
|
+
duplicate
|
17
|
+
out
|
18
|
+
|
19
|
+
# Check whether we've reached Z yet
|
20
|
+
duplicate
|
21
|
+
push "Z"
|
22
|
+
subtract
|
23
|
+
|
24
|
+
# If we have, we're done
|
25
|
+
push done
|
26
|
+
betz
|
27
|
+
|
28
|
+
# Advance to the next letter and loop
|
29
|
+
push 1
|
30
|
+
add
|
31
|
+
push loop
|
32
|
+
jump
|
33
|
+
|
34
|
+
.done
|
35
|
+
# Program ends
|
36
|
+
discard
|
37
|
+
push "\n"
|
38
|
+
out
|
39
|
+
halt
|
40
|
+
|
41
|
+
Run it:
|
42
|
+
|
43
|
+
% gem install base-lang
|
44
|
+
|
45
|
+
% base alphabet.base
|
46
|
+
ABCDEFGHIJKLMNOPQRSTUVWXYZ
|
47
|
+
|
48
|
+
## Language
|
49
|
+
|
50
|
+
All instructions take no arguments except the `push` instruction which takes the value to push to the stack.
|
51
|
+
|
52
|
+
Values may be an integer, a single character in double quotes, a label, or the special text `ip` which refers to
|
53
|
+
the current instruction pointer.
|
54
|
+
|
55
|
+
Signed integers of arbitrary size can be used. Memory locations start at 0 and by default to up to 1MB (1048575).
|
56
|
+
|
57
|
+
operation | op code | stack impact | description
|
58
|
+
-|-|-|-
|
59
|
+
debug | 0 | | enters the debugger
|
60
|
+
push (value or label) | 1, value | + | pushes the value onto the stack
|
61
|
+
discard | 2 | - | discards the top entry on the stack
|
62
|
+
duplicate | 3 | + | duplicates the top entry on the stack
|
63
|
+
write | 4 | -- | writes the second entry on the stack to the memory location at the top entry on the stack
|
64
|
+
read | 5 | -+ | reads from the memory location on the stack and puts the result on the stack
|
65
|
+
add | 6 | --+ | adds the top two entries on the stack and puts the result on the stack
|
66
|
+
subtract | 7 | --+ | subtracts the top entry on the stack from the second entry on the stack and puts the result on the stack
|
67
|
+
jump | 8 | - | jumps to the location indicated by the top entry on the stack
|
68
|
+
bltz | 9 | -- | jumps to the location indicated by the top entry on the stack if the second entry on the stack is less than 0
|
69
|
+
bgtz | 10 | -- | jumps to the location indicated by the top entry on the stack if the second entry on the stack is greater than 0
|
70
|
+
betz | 11 | -- | jumps to the location indicated by the top entry on the stack if the second entry on the stack is equal to 0
|
71
|
+
bnetz | 12 | -- | jumps to the location indicated by the top entry on the stack if the second entry on the stack is not equal to 0
|
72
|
+
out | 13 | - | outputs the top entry on the stack to stdout, interpreted as a unicode character
|
73
|
+
halt | 13 | | halts the program
|
74
|
+
|
75
|
+
## Compiler
|
76
|
+
|
77
|
+
Any text including and after a `#` in the code is treated as a comment and ignored.
|
78
|
+
|
79
|
+
### Labels and data
|
80
|
+
|
81
|
+
You can use labels to mark a place in the code:
|
82
|
+
|
83
|
+
# infinite loop
|
84
|
+
.marker
|
85
|
+
push marker
|
86
|
+
jump
|
87
|
+
|
88
|
+
`.main` is a special label. If specified, code execution will start at this point.
|
89
|
+
|
90
|
+
You can also use labels to introduce data:
|
91
|
+
|
92
|
+
.three_bytes 1, 2, 3
|
93
|
+
|
94
|
+
The data can also be a string, which is interpreted as a list of bytes, or a mix of the two
|
95
|
+
|
96
|
+
.message "Hello!", 10, 0
|
97
|
+
|
98
|
+
### Macros
|
99
|
+
|
100
|
+
Simple macros can be defined as a comma-separated list of operations (or other macros, as long as they don't recurse):
|
101
|
+
|
102
|
+
macro increment push 1, add
|
103
|
+
|
104
|
+
push "A"
|
105
|
+
increment
|
106
|
+
out # outputs a "B"
|
107
|
+
|
108
|
+
You'll need to define a macro before you use it in your file.
|
109
|
+
|
110
|
+
## Debugger
|
111
|
+
|
112
|
+
Base has an integrated debugger that allows you to trace through your code, view memory and stack, and disassemble code.
|
113
|
+
|
114
|
+
You can start it by calling `debug` in your program, or by running `base` with the `--debug` option in which case
|
115
|
+
it'll start immediately on program startup.
|
116
|
+
|
117
|
+
When in the debugger, type `h` for help.
|
118
|
+
|
119
|
+
## Licence and contributing
|
120
|
+
|
121
|
+
MIT licence.
|
122
|
+
|
123
|
+
Feel free to contribute! It's intentionally simple, so the intention isn't to add any more operations than what is
|
124
|
+
there.
|
data/bin/base
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
require_relative "../lib/base"
|
5
|
+
|
6
|
+
options = {}
|
7
|
+
OptionParser.new do |parser|
|
8
|
+
parser.banner = "Usage: base [options] filename.base"
|
9
|
+
|
10
|
+
parser.on("-d", "--debug", "Start debugger immediately") do
|
11
|
+
options[:debug] = true
|
12
|
+
end
|
13
|
+
|
14
|
+
parser.on("-c", "--compile", "Compile only and output program code") do
|
15
|
+
options[:compile] = true
|
16
|
+
end
|
17
|
+
|
18
|
+
parser.on("-h", "--help", "Prints this help") do
|
19
|
+
puts parser
|
20
|
+
exit 1
|
21
|
+
end
|
22
|
+
end.parse!
|
23
|
+
|
24
|
+
if ARGV.length != 1
|
25
|
+
$stderr.puts("one source file must be specified")
|
26
|
+
exit 1
|
27
|
+
end
|
28
|
+
|
29
|
+
begin
|
30
|
+
program = Base::Compiler.new.compile(ARGV[0])
|
31
|
+
|
32
|
+
if options[:compile]
|
33
|
+
puts program.join(",")
|
34
|
+
else
|
35
|
+
Base::VM.new(program, debug: options[:debug] || false).run
|
36
|
+
end
|
37
|
+
|
38
|
+
rescue Base::Compiler::Error, Base::VM::Error => e
|
39
|
+
$stderr.puts("error: #{e.message}")
|
40
|
+
end
|
@@ -0,0 +1,150 @@
|
|
1
|
+
module Base
|
2
|
+
class Compiler
|
3
|
+
Error = Class.new(StandardError)
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@parsing = []
|
7
|
+
@memory = []
|
8
|
+
@labels = {}
|
9
|
+
@macros = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def compile(file)
|
13
|
+
lines = File.read(file).split("\n")
|
14
|
+
parse(lines)
|
15
|
+
|
16
|
+
main = @labels["main"] || 0
|
17
|
+
[main] + @memory
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def parse(lines)
|
23
|
+
lines.each.with_index do |line, index|
|
24
|
+
begin
|
25
|
+
parse_line(line, index + 1)
|
26
|
+
rescue Error => e
|
27
|
+
raise Error, "#{e.message} on line #{index + 1}"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
@parsing.each do |index, line_number|
|
32
|
+
begin
|
33
|
+
@memory[index] = parse_arg(@memory[index], index)
|
34
|
+
rescue Error => e
|
35
|
+
raise Error, "#{e.message} on line #{line_number}"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def parse_line(line, number, macro_entry = [])
|
41
|
+
line = line.gsub(/#(.+)/, '').strip
|
42
|
+
case line
|
43
|
+
when ""
|
44
|
+
nil
|
45
|
+
|
46
|
+
when /\A\.([a-z_][a-z0-9_]*)(?:\s+(.+))?\z/i
|
47
|
+
raise Error, "label already defined" if @labels[$1]
|
48
|
+
raise Error, "can't use 'ip' as a label name" if $1 == 'ip'
|
49
|
+
@labels[$1] = @memory.length
|
50
|
+
@memory += parse_static($2) if $2
|
51
|
+
|
52
|
+
when /\Apush\s+(.+)\z/
|
53
|
+
@memory += [VM::COMMANDS.index("push"), $1]
|
54
|
+
@parsing << [@memory.length - 1, number]
|
55
|
+
|
56
|
+
when /\Amacro\s+(\S+)\s+(.+)/
|
57
|
+
raise Error, "label already defined" if @macros[$1]
|
58
|
+
raise Error, "macros cannot replace base instructions" if VM::COMMANDS.member?($1)
|
59
|
+
@macros[$1] = $2.split(/\s*,\s*/)
|
60
|
+
|
61
|
+
else
|
62
|
+
if op_code = VM::COMMANDS.index(line)
|
63
|
+
@memory << op_code
|
64
|
+
elsif macro = @macros[line]
|
65
|
+
if macro_entry.include?(macro)
|
66
|
+
raise Error, "recursive call to macro #{macro}"
|
67
|
+
end
|
68
|
+
|
69
|
+
macro.each do |macro_line|
|
70
|
+
parse_line(macro_line, number, macro_entry + [macro])
|
71
|
+
end
|
72
|
+
else
|
73
|
+
raise Error, "unknown command '#{line}'"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def parse_arg(arg, location)
|
79
|
+
case arg
|
80
|
+
when '"\n"'
|
81
|
+
"\n".ord
|
82
|
+
when /\A"\\?([^"])"\z/
|
83
|
+
$1.ord
|
84
|
+
when "ip"
|
85
|
+
location - 1
|
86
|
+
when /\A(-?[0-9]+)\z/
|
87
|
+
$1.to_i
|
88
|
+
when /\A([a-z_][a-z0-9_]*)\z/i
|
89
|
+
@labels[$1] or raise Error, "no such label '#{$1}'"
|
90
|
+
else
|
91
|
+
raise Error, "unknown argument '#{arg}'"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def parse_static(arg)
|
96
|
+
chars = arg.chars
|
97
|
+
output = []
|
98
|
+
buffer = ""
|
99
|
+
state = :start
|
100
|
+
|
101
|
+
loop do
|
102
|
+
char = chars.shift
|
103
|
+
if char == "-" && state == :start
|
104
|
+
buffer = "-"
|
105
|
+
state == :number
|
106
|
+
elsif ("0".."9").include?(char) && [:start, :number].include?(state)
|
107
|
+
buffer += char
|
108
|
+
state = :number
|
109
|
+
else
|
110
|
+
if state == :number
|
111
|
+
output << buffer.to_i
|
112
|
+
buffer = ""
|
113
|
+
state = :start
|
114
|
+
end
|
115
|
+
|
116
|
+
if char.nil?
|
117
|
+
if state == :start
|
118
|
+
break output
|
119
|
+
else
|
120
|
+
raise Error, "invalid data"
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
if [',', ' '].include?(char) && state == :start
|
125
|
+
nil
|
126
|
+
elsif char == '"' && state == :start
|
127
|
+
state = :string
|
128
|
+
elsif char == '"' && state == :string
|
129
|
+
output += buffer.chars.map(&:ord)
|
130
|
+
buffer = ""
|
131
|
+
state = :start
|
132
|
+
elsif char == '\\' && state == :string
|
133
|
+
state = :escape
|
134
|
+
elsif state == :string
|
135
|
+
buffer << char
|
136
|
+
elsif state == :escape
|
137
|
+
if char == "n"
|
138
|
+
buffer << "\n"
|
139
|
+
else
|
140
|
+
buffer << char
|
141
|
+
end
|
142
|
+
state = :string
|
143
|
+
else
|
144
|
+
raise Error, "invalid data"
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module Base
|
2
|
+
class Debugger
|
3
|
+
attr_reader :vm
|
4
|
+
|
5
|
+
PUSH = VM::COMMANDS.index("push")
|
6
|
+
|
7
|
+
def initialize(vm)
|
8
|
+
@vm = vm
|
9
|
+
end
|
10
|
+
|
11
|
+
def debug
|
12
|
+
loop do
|
13
|
+
next_instruction = memory[ip] == PUSH ? "push #{memory[ip + 1]}" : VM::COMMANDS[memory[ip]] || "???"
|
14
|
+
puts "ip=#{ip} stack=#{stack.inner} #{next_instruction}"
|
15
|
+
print "> "
|
16
|
+
|
17
|
+
command = $stdin.gets&.strip
|
18
|
+
case command
|
19
|
+
when ""
|
20
|
+
nil
|
21
|
+
when "n"
|
22
|
+
return true
|
23
|
+
when "c", nil
|
24
|
+
return false
|
25
|
+
when "q"
|
26
|
+
exit
|
27
|
+
when /\Am(?:\s+([0-9]+))?(?:\s+([0-9]+))?/
|
28
|
+
memory_dump($1 && $1.to_i || 0, $2 && $2.to_i)
|
29
|
+
when /\Ad(?:\s+([0-9]+))?(?:\s+([0-9]+))?/
|
30
|
+
disassemble($1 && $1.to_i || ip, $2 && $2.to_i)
|
31
|
+
else
|
32
|
+
puts "n - next instruction"
|
33
|
+
puts "c - stop debugging and continue"
|
34
|
+
puts "m [start [length]] - dump memory"
|
35
|
+
puts "d [start [length]] - disassemble"
|
36
|
+
puts "q - quit"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def memory
|
44
|
+
vm.memory
|
45
|
+
end
|
46
|
+
|
47
|
+
def ip
|
48
|
+
vm.ip
|
49
|
+
end
|
50
|
+
|
51
|
+
def stack
|
52
|
+
vm.stack
|
53
|
+
end
|
54
|
+
|
55
|
+
def memory_dump(location, length)
|
56
|
+
data = memory[location..(length ? location + length - 1 : -1)]
|
57
|
+
|
58
|
+
data.each_slice(8).with_index.each do |slice, index|
|
59
|
+
print "%8d | " % (location + index * 8)
|
60
|
+
slice.each do |byte|
|
61
|
+
print "%4d " % byte
|
62
|
+
end
|
63
|
+
puts
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def disassemble(location, length)
|
68
|
+
end_location = length ? location + length : memory.length
|
69
|
+
|
70
|
+
while location < end_location && memory[location]
|
71
|
+
initial_location = location
|
72
|
+
op_code = memory[location]
|
73
|
+
|
74
|
+
if op_code == PUSH
|
75
|
+
value = memory[location + 1]
|
76
|
+
instruction = "push #{value}"
|
77
|
+
location += 2
|
78
|
+
else
|
79
|
+
instruction = VM::COMMANDS[op_code] || "#{op_code}???"
|
80
|
+
location += 1
|
81
|
+
end
|
82
|
+
|
83
|
+
puts "%8d | %s" % [initial_location, instruction]
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
data/lib/base/stack.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
module Base
|
2
|
+
class Stack
|
3
|
+
Error = Class.new(StandardError)
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@stack = []
|
7
|
+
end
|
8
|
+
|
9
|
+
def push(value)
|
10
|
+
@stack.push(value)
|
11
|
+
end
|
12
|
+
|
13
|
+
def pop
|
14
|
+
@stack.pop or raise Error, "pop requested on empty stack"
|
15
|
+
end
|
16
|
+
|
17
|
+
def empty?
|
18
|
+
@stack.empty?
|
19
|
+
end
|
20
|
+
|
21
|
+
def inner
|
22
|
+
@stack
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/base/vm.rb
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
module Base
|
2
|
+
class VM
|
3
|
+
Error = Class.new(StandardError)
|
4
|
+
|
5
|
+
MAX_MEMORY = 1048576
|
6
|
+
|
7
|
+
attr_reader :ip, :stack, :memory
|
8
|
+
|
9
|
+
COMMANDS = %w(debug push discard duplicate write read add subtract jump bltz bgtz betz bnetz out halt)
|
10
|
+
|
11
|
+
def initialize(program, debug: false)
|
12
|
+
@ip = program.shift
|
13
|
+
@memory = program
|
14
|
+
@debug = debug
|
15
|
+
@stack = Stack.new
|
16
|
+
end
|
17
|
+
|
18
|
+
def run
|
19
|
+
loop do
|
20
|
+
raise Error, "IP reached end of memory" if memory[ip].nil?
|
21
|
+
|
22
|
+
@debug = Debugger.new(self).debug if @debug
|
23
|
+
|
24
|
+
begin
|
25
|
+
result = execute
|
26
|
+
break if result == :halt
|
27
|
+
rescue Error => e
|
28
|
+
$stderr.puts("error: #{e.message}")
|
29
|
+
@debug = true
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
raise Error, "warning: stack not empty at program termination, #{stack.inner}" unless @stack.empty?
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def execute
|
39
|
+
case COMMANDS[memory[ip]]
|
40
|
+
when "debug"
|
41
|
+
@debug = true
|
42
|
+
|
43
|
+
when "halt"
|
44
|
+
return :halt
|
45
|
+
|
46
|
+
when "push"
|
47
|
+
@ip += 1
|
48
|
+
arg = memory[ip]
|
49
|
+
stack.push(arg)
|
50
|
+
|
51
|
+
when "discard"
|
52
|
+
stack.pop
|
53
|
+
when "duplicate"
|
54
|
+
value = stack.pop
|
55
|
+
stack.push(value)
|
56
|
+
stack.push(value)
|
57
|
+
|
58
|
+
when "write"
|
59
|
+
location = stack.pop
|
60
|
+
value = stack.pop
|
61
|
+
write(location, value)
|
62
|
+
when "read"
|
63
|
+
location = stack.pop
|
64
|
+
stack.push(read(location))
|
65
|
+
|
66
|
+
when "add"
|
67
|
+
a = stack.pop
|
68
|
+
b = stack.pop
|
69
|
+
stack.push(a + b)
|
70
|
+
when "subtract"
|
71
|
+
a = stack.pop
|
72
|
+
b = stack.pop
|
73
|
+
stack.push(b - a)
|
74
|
+
|
75
|
+
when "jump"
|
76
|
+
@ip = stack.pop - 1
|
77
|
+
when "bltz"
|
78
|
+
dest = stack.pop
|
79
|
+
test = stack.pop
|
80
|
+
@ip = dest - 1 if test < 0
|
81
|
+
when "bgtz"
|
82
|
+
dest = stack.pop
|
83
|
+
test = stack.pop
|
84
|
+
@ip = dest - 1 if test > 0
|
85
|
+
when "betz"
|
86
|
+
dest = stack.pop
|
87
|
+
test = stack.pop
|
88
|
+
@ip = dest - 1 if test == 0
|
89
|
+
when "bnetz"
|
90
|
+
dest = stack.pop
|
91
|
+
test = stack.pop
|
92
|
+
@ip = dest - 1 if test != 0
|
93
|
+
|
94
|
+
when "out"
|
95
|
+
print(stack.pop.chr)
|
96
|
+
else
|
97
|
+
raise Error, "invalid op code #{memory[ip]}"
|
98
|
+
end
|
99
|
+
|
100
|
+
@ip += 1
|
101
|
+
rescue Error, Stack::Error => e
|
102
|
+
raise Error, "#{e.message} at IP #{ip}"
|
103
|
+
end
|
104
|
+
|
105
|
+
private
|
106
|
+
|
107
|
+
def read(location)
|
108
|
+
validate_location(location)
|
109
|
+
memory[location] || 0
|
110
|
+
end
|
111
|
+
|
112
|
+
def write(location, value)
|
113
|
+
validate_location(location)
|
114
|
+
memory[location] = value
|
115
|
+
end
|
116
|
+
|
117
|
+
def validate_location(location)
|
118
|
+
raise Error, "locations must be non-negative" if location < 0
|
119
|
+
raise Error, "locations must be less than #{MAX_MEMORY}" if location >= MAX_MEMORY
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
data/lib/base.rb
ADDED
metadata
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: base-lang
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mog Nesbitt
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2022-03-26 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: A simple stack-based assembly programming language and VM, made for learning.
|
14
|
+
email: mog@seriousorange.com
|
15
|
+
executables:
|
16
|
+
- base
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- README.md
|
21
|
+
- bin/base
|
22
|
+
- lib/base.rb
|
23
|
+
- lib/base/compiler.rb
|
24
|
+
- lib/base/debugger.rb
|
25
|
+
- lib/base/stack.rb
|
26
|
+
- lib/base/vm.rb
|
27
|
+
homepage: https://github.com/mogest/base-lang
|
28
|
+
licenses:
|
29
|
+
- MIT
|
30
|
+
metadata: {}
|
31
|
+
post_install_message:
|
32
|
+
rdoc_options: []
|
33
|
+
require_paths:
|
34
|
+
- lib
|
35
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0'
|
40
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: '0'
|
45
|
+
requirements: []
|
46
|
+
rubygems_version: 3.1.6
|
47
|
+
signing_key:
|
48
|
+
specification_version: 4
|
49
|
+
summary: A simple assembly language, compiler and VM
|
50
|
+
test_files: []
|