Initial data commit
This commit is contained in:
parent
72d218235f
commit
f23f22d71c
199087 changed files with 3378941 additions and 0 deletions
311
Task/Compiler-code-generator/Nim/compiler-code-generator.nim
Normal file
311
Task/Compiler-code-generator/Nim/compiler-code-generator.nim
Normal file
|
|
@ -0,0 +1,311 @@
|
|||
import os, re, streams, strformat, strutils, tables, std/decls
|
||||
|
||||
type
|
||||
|
||||
# AST node types.
|
||||
NodeKind = enum
|
||||
nIdentifier = "Identifier"
|
||||
nString = "String"
|
||||
nInteger = "Integer"
|
||||
nSequence = "Sequence"
|
||||
nIf = "If"
|
||||
nPrtc = "Prtc"
|
||||
nPrts = "Prts"
|
||||
nPrti = "Prti"
|
||||
nWhile = "While"
|
||||
nAssign = "Assign"
|
||||
nNegate = "Negate"
|
||||
nNot = "Not"
|
||||
nMultiply = "Multiply"
|
||||
nDivide = "Divide"
|
||||
nMod = "Mod"
|
||||
nAdd = "Add"
|
||||
nSubtract = "Subtract"
|
||||
nLess = "Less"
|
||||
nLessEqual = "LessEqual"
|
||||
nGreater = "Greater"
|
||||
nGreaterEqual = "GreaterEqual"
|
||||
nEqual = "Equal"
|
||||
nNotEqual = "NotEqual"
|
||||
nAnd = "And"
|
||||
nOr = "Or"
|
||||
|
||||
# Ast node description.
|
||||
Node = ref object
|
||||
left: Node
|
||||
right: Node
|
||||
case kind: NodeKind
|
||||
of nString: stringVal: string
|
||||
of nInteger: intVal: int
|
||||
of nIdentifier: name: string
|
||||
else: nil
|
||||
|
||||
# Virtual machine opcodes.
|
||||
OpCode = enum
|
||||
opFetch = "fetch"
|
||||
opStore = "store"
|
||||
opPush = "push"
|
||||
opJmp = "jmp"
|
||||
opJz = "jz"
|
||||
opAdd = "add"
|
||||
opSub = "sub"
|
||||
opMul = "mul"
|
||||
opDiv = "div"
|
||||
opMod = "mod"
|
||||
opLt = "lt"
|
||||
opgt = "gt"
|
||||
opLe = "le"
|
||||
opGe = "ge"
|
||||
opEq = "eq"
|
||||
opNe = "ne"
|
||||
opAnd = "and"
|
||||
opOr = "or"
|
||||
opNeg = "neg"
|
||||
opNot = "not"
|
||||
opPrtc = "prtc"
|
||||
opPrti = "prti"
|
||||
opPrts = "prts"
|
||||
opHalt = "halt"
|
||||
opInvalid = "invalid"
|
||||
|
||||
# Code generator context.
|
||||
CodeGen = object
|
||||
address: int # Current address in code part.
|
||||
instr: seq[string] # List of instructions.
|
||||
vars: Table[string, int] # Mapping variable name -> variable index.
|
||||
strings: seq[string] # List of strings.
|
||||
|
||||
# Node ranges.
|
||||
UnaryOpNode = range[nNegate..nNot]
|
||||
BinaryOpNode = range[nMultiply..nOr]
|
||||
PrintNode = range[nPrtc..nPrti]
|
||||
|
||||
|
||||
const
|
||||
|
||||
# Mapping unary operator Node -> OpCode.
|
||||
UnOp: array[UnaryOpNode, OpCode] = [opNeg, opNot]
|
||||
|
||||
# Mapping binary operator Node -> OpCode.
|
||||
BinOp: array[BinaryOpNode, OpCode] = [opMul, opDiv, opMod, opAdd, opSub, opLt,
|
||||
opLe, opGt, opGe, opEq, opNe, opAnd, opOr]
|
||||
|
||||
# Mapping print Node -> OpCode.
|
||||
PrintOp: array[PrintNode, OpCode] = [opPrtc, opPrts, opPrti]
|
||||
|
||||
|
||||
####################################################################################################
|
||||
# Code generator.
|
||||
|
||||
proc genSimpleInst(gen: var CodeGen; opcode: OpCode) =
|
||||
## Build a simple instruction (no operand).
|
||||
gen.instr.add &"{gen.address:>5} {opcode}"
|
||||
|
||||
#---------------------------------------------------------------------------------------------------
|
||||
|
||||
proc genMemInst(gen: var CodeGen; opcode: OpCode; memIndex: int) =
|
||||
## Build a memory access instruction (opFetch, opStore).
|
||||
gen.instr.add &"{gen.address:>5} {opcode:<5} [{memIndex}]"
|
||||
|
||||
#---------------------------------------------------------------------------------------------------
|
||||
|
||||
proc genJumpInst(gen: var CodeGen; opcode: OpCode): int =
|
||||
## Build a jump instruction. We use the letters X and Y as placeholders
|
||||
## for the offset and the target address.
|
||||
result = gen.instr.len
|
||||
gen.instr.add &"{gen.address:>5} {opcode:<5} (X) Y"
|
||||
|
||||
#---------------------------------------------------------------------------------------------------
|
||||
|
||||
proc genPush(gen: var CodeGen; value: int) =
|
||||
## Build a push instruction.
|
||||
gen.instr.add &"{gen.address:>5} {opPush:<5} {value}"
|
||||
|
||||
#---------------------------------------------------------------------------------------------------
|
||||
|
||||
proc updateJumpInst(gen: var CodeGen; index: int; jumpAddress, targetAddress: int) =
|
||||
## Update the offset and the target address of a jump instruction.
|
||||
|
||||
var instr {.byAddr.} = gen.instr[index]
|
||||
let offset = targetAddress - jumpAddress - 1
|
||||
for idx in countdown(instr.high, 0):
|
||||
case instr[idx]
|
||||
of 'Y':
|
||||
instr[idx..idx] = $targetAddress
|
||||
of 'X':
|
||||
instr[idx..idx] = $offset
|
||||
break
|
||||
else:
|
||||
discard
|
||||
|
||||
#---------------------------------------------------------------------------------------------------
|
||||
|
||||
proc process(gen: var CodeGen; node: Node) =
|
||||
## Generate code for a node.
|
||||
|
||||
if node.isNil: return
|
||||
|
||||
case node.kind:
|
||||
|
||||
of nInteger:
|
||||
gen.genPush(node.intVal)
|
||||
inc gen.address, 5
|
||||
|
||||
of nIdentifier:
|
||||
if node.name notin gen.vars:
|
||||
gen.vars[node.name] = gen.vars.len
|
||||
gen.genMemInst(opFetch, gen.vars[node.name])
|
||||
inc gen.address, 5
|
||||
|
||||
of nString:
|
||||
var index = gen.strings.find(node.stringVal)
|
||||
if index < 0:
|
||||
index = gen.strings.len
|
||||
gen.strings.add(node.stringVal)
|
||||
gen.genPush(index)
|
||||
inc gen.address, 5
|
||||
|
||||
of nAssign:
|
||||
gen.process(node.right)
|
||||
if node.left.name notin gen.vars:
|
||||
gen.vars[node.left.name] = gen.vars.len
|
||||
gen.genMemInst(opStore, gen.vars[node.left.name])
|
||||
inc gen.address, 5
|
||||
|
||||
of UnaryOpNode.low..UnaryOpNode.high:
|
||||
gen.process(node.left)
|
||||
gen.genSimpleInst(UnOp[node.kind])
|
||||
inc gen.address
|
||||
|
||||
of BinaryOpNode.low..BinaryOpNode.high:
|
||||
gen.process(node.left)
|
||||
gen.process(node.right)
|
||||
gen.genSimpleInst(BinOp[node.kind])
|
||||
inc gen.address
|
||||
|
||||
of PrintNode.low..PrintNode.high:
|
||||
gen.process(node.left)
|
||||
gen.genSimpleInst(PrintOp[node.kind])
|
||||
inc gen.address
|
||||
|
||||
of nIf:
|
||||
# Generate condition expression.
|
||||
gen.process(node.left)
|
||||
# Generate jump if zero.
|
||||
let jzAddr = gen.address
|
||||
let jzInst = gen.genJumpInst(opJz)
|
||||
inc gen.address, 5
|
||||
# Generate then branch expression.
|
||||
gen.process(node.right.left)
|
||||
# If there is an "else" clause, generate unconditional jump
|
||||
var jmpAddr, jmpInst: int
|
||||
let hasElseClause = not node.right.right.isNil
|
||||
if hasElseClause:
|
||||
jmpAddr = gen.address
|
||||
jmpInst = gen.genJumpInst(opJmp)
|
||||
inc gen.address, 5
|
||||
# Update JZ offset.
|
||||
gen.updateJumpInst(jzInst, jzAddr, gen.address)
|
||||
# Generate else expression.
|
||||
if hasElseClause:
|
||||
gen.process(node.right.right)
|
||||
# Update JMP offset.
|
||||
gen.updateJumpInst(jmpInst, jmpAddr, gen.address)
|
||||
|
||||
of nWhile:
|
||||
let condAddr = gen.address
|
||||
# Generate condition expression.
|
||||
gen.process(node.left)
|
||||
# Generate jump if zero.
|
||||
let jzAddr = gen.address
|
||||
let jzInst = gen.genJumpInst(opJz)
|
||||
inc gen.address, 5
|
||||
# Generate loop code.
|
||||
gen.process(node.right)
|
||||
# Generate unconditional jump.
|
||||
let jmpAddr = gen.address
|
||||
let jmpInst = gen.genJumpInst(opJmp)
|
||||
inc gen.address, 5
|
||||
# Update JMP offset.
|
||||
gen.updateJumpInst(jmpInst, jmpAddr, condAddr)
|
||||
# Update JZ offset.
|
||||
gen.updateJumpInst(jzInst, jzAddr, gen.address)
|
||||
|
||||
of nSequence:
|
||||
gen.process(node.left)
|
||||
gen.process(node.right)
|
||||
|
||||
#---------------------------------------------------------------------------------------------------
|
||||
|
||||
proc run(gen: var CodeGen; ast: Node) =
|
||||
## Run the code generator on the AST.
|
||||
|
||||
# Process recursively the nodes.
|
||||
gen.process(ast)
|
||||
gen.genSimpleInst(opHalt) # Add a Halt operator at the end.
|
||||
|
||||
# Output header.
|
||||
echo &"Datasize: {gen.vars.len} Strings: {gen.strings.len}"
|
||||
# Output strings.
|
||||
for s in gen.strings:
|
||||
echo s.escape().replace("\\x0A", "\\n")
|
||||
# Output code.
|
||||
for inst in gen.instr:
|
||||
echo inst
|
||||
|
||||
####################################################################################################
|
||||
# AST loader.
|
||||
|
||||
proc newNode(kind: NodeKind; left: Node; right: Node = nil): Node =
|
||||
## Create a new node with given left and right children.
|
||||
result = Node(kind: kind, left: left, right: right)
|
||||
|
||||
#---------------------------------------------------------------------------------------------------
|
||||
|
||||
proc loadAst(stream: Stream): Node =
|
||||
## Load a linear AST and build a binary tree.
|
||||
|
||||
let line = stream.readLine().strip()
|
||||
if line.startsWith(';'):
|
||||
return nil
|
||||
|
||||
var fields = line.split(' ', 1)
|
||||
let kind = parseEnum[NodeKind](fields[0])
|
||||
if kind in {nIdentifier, nString, nInteger}:
|
||||
if fields.len < 2:
|
||||
raise newException(ValueError, "Missing value field for " & fields[0])
|
||||
else:
|
||||
fields[1] = fields[1].strip()
|
||||
case kind
|
||||
of nIdentifier:
|
||||
return Node(kind: nIdentifier, name: fields[1])
|
||||
of nString:
|
||||
let str = fields[1].replacef(re"([^\\])(\\n)", "$1\n").replace(r"\\", r"\").replace("\"", "")
|
||||
return Node(kind: nString, stringVal: str)
|
||||
of nInteger:
|
||||
return Node(kind: nInteger, intVal: parseInt(fields[1]))
|
||||
else:
|
||||
if fields.len > 1:
|
||||
raise newException(ValueError, "Extra field for " & fields[0])
|
||||
|
||||
let left = stream.loadAst()
|
||||
let right = stream.loadAst()
|
||||
result = newNode(kind, left, right)
|
||||
|
||||
|
||||
#———————————————————————————————————————————————————————————————————————————————————————————————————
|
||||
|
||||
var stream: Stream
|
||||
var toClose = false
|
||||
var codegen: CodeGen
|
||||
|
||||
if paramCount() < 1:
|
||||
stream = newFileStream(stdin)
|
||||
else:
|
||||
stream = newFileStream(paramStr(1))
|
||||
toClose = true
|
||||
|
||||
let ast = loadAst(stream)
|
||||
if toClose: stream.close()
|
||||
|
||||
codegen.run(ast)
|
||||
Loading…
Add table
Add a link
Reference in a new issue