base-lang 0.1.0
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 +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: []
|