Finished loading
Need to decide on how to store pointers before continuing.
This commit is contained in:
parent
ceb23440f0
commit
a89fe4f0c6
16
arctic.nimble
Normal file
16
arctic.nimble
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
# Package
|
||||||
|
|
||||||
|
version = "0.1.0"
|
||||||
|
author = "Louis Burke"
|
||||||
|
description = "ARCTIC in Nim"
|
||||||
|
license = "Apache-2.0"
|
||||||
|
srcDir = "src"
|
||||||
|
installExt = @["nim"]
|
||||||
|
bin = @["arctic"]
|
||||||
|
|
||||||
|
|
||||||
|
# Dependencies
|
||||||
|
|
||||||
|
requires "nim >= 2.0.0"
|
||||||
|
requires "bio"
|
||||||
|
requires "itertools"
|
333
src/arctic.nim
Normal file
333
src/arctic.nim
Normal file
|
@ -0,0 +1,333 @@
|
||||||
|
import std/[critbits, strformat, strutils, strmisc, re]
|
||||||
|
import bio
|
||||||
|
import itertools
|
||||||
|
|
||||||
|
type
|
||||||
|
ArcticTypeKind* = enum
|
||||||
|
BYTE, SHORT, INT, WORD, DOUBLE
|
||||||
|
|
||||||
|
ArcticType* {.union.} = object
|
||||||
|
b: int8
|
||||||
|
s: int16
|
||||||
|
i: int32
|
||||||
|
w: int64
|
||||||
|
d: float64
|
||||||
|
p: pointer # only used for dynamically allocated memory
|
||||||
|
f: ArcticSymbol # also used for pointers to section memory
|
||||||
|
|
||||||
|
ArcticVariableIndex* = enum
|
||||||
|
VARIABLE_A, VARIABLE_B, VARIABLE_C,
|
||||||
|
VARIABLE_I, VARIABLE_N,
|
||||||
|
VARIABLE_X, VARIABLE_Y, VARIABLE_Z
|
||||||
|
|
||||||
|
ArcticSymbol* = tuple
|
||||||
|
section: string
|
||||||
|
index: int
|
||||||
|
|
||||||
|
ArcticImmediateKind* = enum
|
||||||
|
PLAIN, VARIABLE, INTEGER, NUMBER, SYMBOL
|
||||||
|
|
||||||
|
ArcticImmediate* = object
|
||||||
|
case kind: ArcticImmediateKind
|
||||||
|
of PLAIN: nil
|
||||||
|
of VARIABLE: v: ArcticVariableIndex
|
||||||
|
of INTEGER: i: int64
|
||||||
|
of NUMBER: n: float64
|
||||||
|
of SYMBOL: s: string
|
||||||
|
|
||||||
|
ArcticOperation* = object
|
||||||
|
code: char
|
||||||
|
immediate: ArcticImmediate
|
||||||
|
|
||||||
|
ArcticCode* = seq[ArcticOperation]
|
||||||
|
ArcticMemory* = seq[uint8]
|
||||||
|
|
||||||
|
ArcticSection* = object
|
||||||
|
case iscode: bool
|
||||||
|
of true: code: ArcticCode
|
||||||
|
of false: data: ArcticMemory
|
||||||
|
ArcticStack* = seq[ArcticType]
|
||||||
|
|
||||||
|
ArcticState* = object
|
||||||
|
symbols: CritBitTree[ArcticSymbol] # as name or section _name
|
||||||
|
sections: CritBitTree[ArcticSection]
|
||||||
|
stack: ArcticStack
|
||||||
|
pc: ArcticSymbol
|
||||||
|
registers: array[ArcticVariableIndex, ArcticType]
|
||||||
|
|
||||||
|
ArcticStepResult* = enum
|
||||||
|
CONTINUE, BREAKPOINT, ERROR
|
||||||
|
|
||||||
|
ArcticBuiltin* = proc (state: var ArcticState): ArcticStepResult
|
||||||
|
|
||||||
|
const
|
||||||
|
ImmediateOps: set[char] = {'0', 'E', 'F', 'G', 'H', 'L', 'M', 'P', 'Q', 'd', 'o', 'u', '<', '{', '=', '}', '>', ',', ';', '[', ']'}
|
||||||
|
PlainOps : set[char] = { '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'I', 'J', 'K', 'N', 'O', 'R', 'S', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'p', 'q', 'r', 's', 't', 'v', 'w', 'x', 'y', 'z', '_', '.', '+', '-', '*', '/', '%', '\\', '|', '&', '^', '!', '?', '(', ')', '`' }
|
||||||
|
|
||||||
|
proc `$`*(variable: ArcticVariableIndex): string =
|
||||||
|
case variable:
|
||||||
|
of VARIABLE_A: return "A"
|
||||||
|
of VARIABLE_B: return "B"
|
||||||
|
of VARIABLE_C: return "C"
|
||||||
|
of VARIABLE_I: return "I"
|
||||||
|
of VARIABLE_N: return "N"
|
||||||
|
of VARIABLE_X: return "X"
|
||||||
|
of VARIABLE_Y: return "Y"
|
||||||
|
of VARIABLE_Z: return "Z"
|
||||||
|
|
||||||
|
proc `$`*(op: ArcticOperation): string =
|
||||||
|
result &= op.code
|
||||||
|
|
||||||
|
case op.immediate.kind:
|
||||||
|
of PLAIN: discard
|
||||||
|
of VARIABLE: result &= $op.immediate.v
|
||||||
|
of INTEGER: result &= $op.immediate.i
|
||||||
|
of NUMBER: result &= $op.immediate.n
|
||||||
|
of SYMBOL: result &= "$" & $op.immediate.s
|
||||||
|
|
||||||
|
result &= " "
|
||||||
|
|
||||||
|
proc `$`*(state: ArcticState): string =
|
||||||
|
result &= "Registers:\n"
|
||||||
|
for idx in ArcticVariableIndex:
|
||||||
|
result &= &" {idx} = {state.registers[idx]}\n"
|
||||||
|
|
||||||
|
result &= "Stack:\n"
|
||||||
|
|
||||||
|
for item in state.stack:
|
||||||
|
result &= &" {item}\n"
|
||||||
|
|
||||||
|
for (name, section) in state.sections.pairs:
|
||||||
|
result &= &"\n\"{name}\": "
|
||||||
|
if section.iscode:
|
||||||
|
result &= "code \n"
|
||||||
|
for (i, m) in section.code.pairs:
|
||||||
|
for (label, location) in state.symbols.pairs:
|
||||||
|
if location.section == name and location.index == i:
|
||||||
|
result &= &":{label} "
|
||||||
|
if state.pc.section == name and state.pc.index == i:
|
||||||
|
result &= ">"
|
||||||
|
result &= $m
|
||||||
|
else:
|
||||||
|
result &= "\n"
|
||||||
|
for (i, m) in section.data.pairs:
|
||||||
|
for (label, location) in state.symbols.pairs:
|
||||||
|
if location.section == name and location.index == i:
|
||||||
|
result &= &":{label} "
|
||||||
|
result &= &"{m} "
|
||||||
|
|
||||||
|
proc op(code: char, immediate: ArcticImmediate = ArcticImmediate(kind: PLAIN)): ArcticOperation =
|
||||||
|
return ArcticOperation(code: code, immediate: immediate)
|
||||||
|
|
||||||
|
proc add(section: var ArcticSection, code: char) =
|
||||||
|
if section.iscode:
|
||||||
|
section.code.add code.op
|
||||||
|
else:
|
||||||
|
section.data.add code.uint8
|
||||||
|
|
||||||
|
proc tovar(code: char): ArcticVariableIndex =
|
||||||
|
case code.toLowerAscii:
|
||||||
|
of 'a': VARIABLE_A
|
||||||
|
of 'b': VARIABLE_B
|
||||||
|
of 'c': VARIABLE_C
|
||||||
|
of 'i': VARIABLE_I
|
||||||
|
of 'n': VARIABLE_N
|
||||||
|
of 'x': VARIABLE_X
|
||||||
|
of 'y': VARIABLE_Y
|
||||||
|
of 'z': VARIABLE_Z
|
||||||
|
else: raise new ValueError
|
||||||
|
|
||||||
|
proc add(section: var ArcticSection, code: char, immstr: string, secname: string) =
|
||||||
|
if section.iscode:
|
||||||
|
if immstr.match(re"^\d+$"):
|
||||||
|
section.code.add code.op(ArcticImmediate(kind: INTEGER, i: immstr.parseInt))
|
||||||
|
elif immstr.match(re"^[AaBbCcIiNnXxYyZz]$"):
|
||||||
|
section.code.add code.op(ArcticImmediate(kind: VARIABLE, v: immstr[0].tovar))
|
||||||
|
elif immstr[0] == '$':
|
||||||
|
if immstr.len == 1 or immstr[1] == '_':
|
||||||
|
section.code.add code.op(ArcticImmediate(kind: SYMBOL, s: secname & " " & immstr))
|
||||||
|
else:
|
||||||
|
section.code.add code.op(ArcticImmediate(kind: SYMBOL, s: immstr))
|
||||||
|
else:
|
||||||
|
section.code.add code.op(ArcticImmediate(kind: NUMBER, n: immstr.parseFloat))
|
||||||
|
|
||||||
|
proc len(section: ArcticSection): int =
|
||||||
|
if section.iscode:
|
||||||
|
return section.code.len
|
||||||
|
else:
|
||||||
|
return section.data.len
|
||||||
|
|
||||||
|
func grabnum(data: string): (string, string) =
|
||||||
|
## Returns the numeric literal at the start of data, then the rest of data
|
||||||
|
var i = 0
|
||||||
|
while i < data.len and data[i] in "0123456789.":
|
||||||
|
result[0] &= data[i]
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
result[1] = data[i..^1]
|
||||||
|
|
||||||
|
proc insert(section: var ArcticSection, data: string) =
|
||||||
|
if data.len == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
if section.iscode:
|
||||||
|
return
|
||||||
|
|
||||||
|
template insertInt(itype, utype, endianness) =
|
||||||
|
let (text, rest) = data[1..^1].grabnum
|
||||||
|
|
||||||
|
if text[0] == '-':
|
||||||
|
let val: itype = itype(-text[1..^1].parseBiggestInt)
|
||||||
|
section.data.add cast[utype](val).serialize(endianness)
|
||||||
|
else:
|
||||||
|
let val: utype = utype(text.parseBiggestUInt)
|
||||||
|
section.data.add val.serialize(endianness)
|
||||||
|
|
||||||
|
section.insert(rest)
|
||||||
|
|
||||||
|
case data[0]:
|
||||||
|
of 'i': insertInt(int64, uint64, littleEndian)
|
||||||
|
of 'I': insertInt(int64, uint64, bigEndian)
|
||||||
|
of 'w': insertInt(int32, uint32, littleEndian)
|
||||||
|
of 'W': insertInt(int32, uint32, bigEndian)
|
||||||
|
of 's': insertInt(int16, uint16, littleEndian)
|
||||||
|
of 'S': insertInt(int16, uint16, bigEndian)
|
||||||
|
of 'b', 'B': # bytes don't have an endianness
|
||||||
|
let (text, rest) = data[1..^1].grabnum
|
||||||
|
|
||||||
|
if text[0] == '-':
|
||||||
|
let val: int8 = int8(text[1..^1].parseBiggestInt)
|
||||||
|
section.data.add cast[uint8](val)
|
||||||
|
else:
|
||||||
|
let val: uint8 = uint8(text.parseBiggestUInt)
|
||||||
|
section.data.add val
|
||||||
|
|
||||||
|
of 'f':
|
||||||
|
let (text, rest) = data[1..^1].grabnum
|
||||||
|
section.data.add cast[uint64](text.parseFloat).serialize(littleEndian)
|
||||||
|
section.insert(rest)
|
||||||
|
of 'F':
|
||||||
|
let (text, rest) = data[1..^1].grabnum
|
||||||
|
section.data.add cast[uint64](text.parseFloat).serialize(bigEndian)
|
||||||
|
section.insert(rest)
|
||||||
|
|
||||||
|
of 'x': # hexadecimal byte constant
|
||||||
|
let (hex, x, rest) = data[1..^1].partition("x")
|
||||||
|
for pair in hex.chunked(2):
|
||||||
|
section.data.add uint8(parseHexInt(pair.join))
|
||||||
|
section.insert(rest)
|
||||||
|
|
||||||
|
of '"': # utf-8 string constant with escapes
|
||||||
|
let strlen = data.matchLen(re"^""([^""]|\\"")*""")
|
||||||
|
|
||||||
|
if strlen < 0:
|
||||||
|
return # TODO: couldn't find a string, what to do?
|
||||||
|
|
||||||
|
for c in data[1..^(strlen-2)]:
|
||||||
|
section.data.add c.uint8
|
||||||
|
|
||||||
|
section.insert(data[strlen..^1])
|
||||||
|
|
||||||
|
else:
|
||||||
|
section.insert(data[1..^1])
|
||||||
|
|
||||||
|
func load*(code: string): ArcticState =
|
||||||
|
var
|
||||||
|
section: string = ""
|
||||||
|
token: string = ""
|
||||||
|
|
||||||
|
result.pc = ("", 0)
|
||||||
|
result.sections[section] = ArcticSection(iscode: true, code: @[])
|
||||||
|
|
||||||
|
for next in code:
|
||||||
|
debug_echo "token: ", token, ", next: ", next
|
||||||
|
if token.len == 0: # initial state
|
||||||
|
case next:
|
||||||
|
of ImmediateOps, '#', ':', '\"', '\'':
|
||||||
|
token &= next
|
||||||
|
of PlainOps, '\n':
|
||||||
|
result.sections[section].add next
|
||||||
|
else:
|
||||||
|
discard
|
||||||
|
else:
|
||||||
|
case token[0]:
|
||||||
|
of '#': # comment
|
||||||
|
if next == '\n':
|
||||||
|
token = ""
|
||||||
|
result.sections[section].add next
|
||||||
|
|
||||||
|
of '"': # section switch
|
||||||
|
if next == '"' and token[^1] != '\\':
|
||||||
|
section = token[1..^1]
|
||||||
|
if section notin result.sections:
|
||||||
|
if section.toLowerAscii in @["code", "macros", ""]:
|
||||||
|
result.sections[section] = ArcticSection(iscode: true, code: @[])
|
||||||
|
else:
|
||||||
|
result.sections[section] = ArcticSection(iscode: false, data: @[])
|
||||||
|
token = ""
|
||||||
|
else:
|
||||||
|
token.add next
|
||||||
|
|
||||||
|
of ':': # label name
|
||||||
|
if next == ' ' or next == '\n':
|
||||||
|
let idx = if token[1] == '_': section & " " & token[1..^1] else: token[1..^1]
|
||||||
|
result.symbols[idx] = (section: section, index: result.sections[section].len)
|
||||||
|
token = ""
|
||||||
|
else:
|
||||||
|
token.add next
|
||||||
|
|
||||||
|
of '\'': # data injection
|
||||||
|
if next == '\'':
|
||||||
|
var
|
||||||
|
quoted = false
|
||||||
|
escaped = false
|
||||||
|
for c in token:
|
||||||
|
if c == '"':
|
||||||
|
if not escaped:
|
||||||
|
quoted = not quoted
|
||||||
|
escaped = false
|
||||||
|
elif c == '\\':
|
||||||
|
escaped = true
|
||||||
|
else:
|
||||||
|
escaped = false
|
||||||
|
|
||||||
|
if quoted or escaped:
|
||||||
|
token.add next
|
||||||
|
else:
|
||||||
|
result.sections[section].insert token[1..^1]
|
||||||
|
token = ""
|
||||||
|
else:
|
||||||
|
token.add next
|
||||||
|
|
||||||
|
of ImmediateOps:
|
||||||
|
if next == ' ':
|
||||||
|
result.sections[section].add(token[0], token[1..^1], section)
|
||||||
|
token = ""
|
||||||
|
else:
|
||||||
|
token.add next
|
||||||
|
|
||||||
|
else:
|
||||||
|
discard
|
||||||
|
|
||||||
|
proc step*(state: var ArcticState, builtins: CritBitTree[ArcticBuiltin]): ArcticStepResult =
|
||||||
|
let codesec = state.sections[state.pc.section]
|
||||||
|
|
||||||
|
if not codesec.iscode:
|
||||||
|
return ERROR
|
||||||
|
|
||||||
|
if state.pc.index >= codesec.code.len:
|
||||||
|
return ERROR
|
||||||
|
|
||||||
|
let op = codesec.code[state.pc.index]
|
||||||
|
case op.code:
|
||||||
|
case '1': # LDAB
|
||||||
|
let top = state.stack[^1]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## TODO
|
||||||
|
|
||||||
|
|
||||||
|
when isMainModule:
|
||||||
|
let state = stdin.readAll.load
|
||||||
|
echo state
|
Loading…
Reference in a new issue