STUDIO HAZE · jeniapp
Topaz source
Open by house policy. Topaz is our own language.
Topaz 5.20.2 · 63 files
apps/circuit/core/netlist.tpz
Topaz · 7330 B · Original file
1
// 회로 그래프. 노드와 전선의 자료형, node 줄과 wire 줄 파서, 전선 검증을 둡니다.2
// node 아이디 종류 x y [값] 줄과 wire 시작:핀 끝:핀 줄을 토큰으로 받아 회로나 첫 오류 문구를 돌려줍니다.3
// 같은 입력이면 늘 같은 회로와 같은 오류가 나옵니다. 좌표는 표시용 값이고 신호 계산에 쓰지 않습니다.4
// 칩 계층과 평가는 다루지 않습니다. 그 일은 core.hierarchy 와 core.sim 이 합니다.5
// Circuit graph: node and wire types, node/wire line parsers and wire validation.7
import core.gate8
import core.util10
export type 노드종류 = "INPUT" | "OUTPUT" | "SEG7" | "NAND" | "AND" | "OR" | "NOT" | "XOR" | "NOR" | "XNOR"11
export type 노드 = { 아이디: string, 종류: 노드종류, x: int, y: int, 값: int }12
export type 전선 = { 시작노드: string, 시작핀: string, 끝노드: string, 끝핀: string }13
export type 회로 = { 노드들: Array<노드>, 전선들: Array<전선> }14
export type 입력값 = { 아이디: string, 값: int }16
type 끝점 = { 노드: string, 핀: string }18
export let 세그핀들 = ["in0", "in1", "in2", "in3", "in4", "in5", "in6"]20
function 노드종류로(토큰: string) -> Result<노드종류, string> {21
match 토큰 {22
case "INPUT" => Ok("INPUT")23
case "OUTPUT" => Ok("OUTPUT")24
case "SEG7" => Ok("SEG7")25
case "NAND" => Ok("NAND")26
case "AND" => Ok("AND")27
case "OR" => Ok("OR")28
case "NOT" => Ok("NOT")29
case "XOR" => Ok("XOR")30
case "NOR" => Ok("NOR")31
case "XNOR" => Ok("XNOR")32
case _ => Err("알 수 없는 게이트 종류입니다: " + 토큰)33
}34
}36
// 목록의 길이가 평가에 쓰는 입력 수입니다.37
export function 입력핀들(종류: 노드종류) -> Array<string> {38
match 종류 {39
case "INPUT" => []40
case "OUTPUT" | "NOT" => ["in0"]41
case "SEG7" => 세그핀들42
case "NAND" | "AND" | "OR" | "XOR" | "NOR" | "XNOR" => ["in0", "in1"]43
}44
}46
function 출력핀인가(종류: 노드종류, 핀: string) -> bool {47
match 종류 {48
case "OUTPUT" | "SEG7" => false49
case "INPUT" | "NAND" | "AND" | "OR" | "NOT" | "XOR" | "NOR" | "XNOR" => 핀 == "out"50
}51
}53
// SEG7 은 출력 핀이 없는 표시 장치라 자기 신호가 늘 꺼져 있습니다.54
export function 출력계산(종류: 노드종류, 가: bool, 나: bool) -> bool {55
match 종류 {56
case "INPUT" | "OUTPUT" => 가57
case "SEG7" => false58
case "NAND" => gate.부정논리곱(가, 나)59
case "AND" => gate.논리곱(가, 나)60
case "OR" => gate.논리합(가, 나)61
case "NOT" => gate.부정(가)62
case "XOR" => gate.배타적논리합(가, 나)63
case "NOR" => gate.부정논리합(가, 나)64
case "XNOR" => gate.배타적부정논리합(가, 나)65
}66
}68
export function 입력노드인가(종류: 노드종류) -> bool { 종류 == "INPUT" }70
export function 출력노드인가(종류: 노드종류) -> bool { 종류 == "OUTPUT" }72
// 같은 입력이 여럿이면 마지막 값을 씁니다. 목록에 없는 입력은 원래 값을 지킵니다.73
export function 입력적용(노드들: Array<노드>, 입력값들: Array<입력값>) -> Array<노드> {74
let mut 값표: Map<string, int> = Map.new()75
for 입력 in 입력값들 { 값표.insert(입력.아이디, 입력.값) }76
노드들.map(노드값 => if 입력노드인가(노드값.종류) { 노드값{ 값: 값표.getOr(노드값.아이디, 노드값.값) } } else { 노드값 })77
}79
// 값은 INPUT 에만 붙고 0 또는 1 입니다.80
function 노드줄파싱(토큰들: Array<string>) -> Result<노드, string> {81
if 토큰들.length != 5 && 토큰들.length != 6 { return Err("node 줄 형식은 node 아이디 종류 x y [값] 입니다") }82
if !util.안전아이디인가(토큰들[1]) { return Err("유효하지 않은 노드 아이디입니다: " + 토큰들[1]) }83
let 종류 = 노드종류로(토큰들[2])?84
if !util.정수인가(토큰들[3]) || !util.정수인가(토큰들[4]) { return Err("좌표는 정수여야 합니다") }85
if 토큰들.length == 6 && !입력노드인가(종류) { return Err("값은 INPUT 노드에만 붙습니다") }86
if 토큰들.length == 6 && 토큰들[5] != "0" && 토큰들[5] != "1" { return Err("입력 값은 0 또는 1이어야 합니다") }87
Ok({88
아이디: 토큰들[1],89
종류: 종류,90
x: toInt(토큰들[3]) ?? 0,91
y: toInt(토큰들[4]) ?? 0,92
값: if 토큰들.length == 6 && 토큰들[5] == "1" { 1 } else { 0 },93
})94
}96
function 끝점파싱(토큰: string) -> Result<끝점, string> {97
let 부분 = 토큰.split(":")98
if 부분.length != 2 || 부분[0] == "" || 부분[1] == "" { return Err("전선 끝점은 노드:핀 형식입니다") }99
Ok({ 노드: 부분[0], 핀: 부분[1] })100
}102
export function 전선줄파싱(토큰들: Array<string>) -> Result<전선, string> {103
if 토큰들.length != 3 { return Err("wire 줄 형식은 wire 시작:핀 끝:핀 입니다") }104
let 시작 = 끝점파싱(토큰들[1])?105
let 끝 = 끝점파싱(토큰들[2])?106
Ok({ 시작노드: 시작.노드, 시작핀: 시작.핀, 끝노드: 끝.노드, 끝핀: 끝.핀 })107
}109
function 종류찾기(종류표: Map<string, 노드종류>, 아이디: string) -> Result<노드종류, string> {110
match 종류표.get(아이디) {111
case Some(종류) => Ok(종류)112
case None => Err("전선이 없는 노드를 가리킵니다: " + 아이디)113
}114
}116
// 앞 전선부터 끝점과 핀을 보고, 모두 맞으면 한 입력 핀을 두 전선이 구동하는 첫 전선을 찾습니다.117
function 전선검증(노드들: Array<노드>, 전선들: Array<전선>) -> Result<회로, string> {118
let mut 종류표: Map<string, 노드종류> = Map.new()119
for 노드값 in 노드들 { 종류표.insert(노드값.아이디, 노드값.종류) }120
for 선 in 전선들 {121
let 시작종류 = 종류찾기(종류표, 선.시작노드)?122
let 끝종류 = 종류찾기(종류표, 선.끝노드)?123
if !출력핀인가(시작종류, 선.시작핀) { return Err("전선 시작은 출력 핀이어야 합니다: " + 선.시작노드 + ":" + 선.시작핀) }124
if !(선.끝핀 in 입력핀들(끝종류)) { return Err("전선 끝은 입력 핀이어야 합니다: " + 선.끝노드 + ":" + 선.끝핀) }125
}126
let 핀키들 = 전선들.map(선 => 선.끝노드 + ":" + 선.끝핀)127
let mut 구동수: Map<string, int> = Map.new()128
for 핀키 in 핀키들 { 구동수.insert(핀키, 구동수.getOr(핀키, 0) + 1) }129
let 겹친핀들 = 핀키들.filter(핀키 => 구동수.getOr(핀키, 0) > 1)130
if 겹친핀들.length > 0 { return Err("한 입력 핀에 전선이 둘 이상입니다: " + 겹친핀들[0]) }131
Ok({ 노드들: 노드들, 전선들: 전선들 })132
}134
// node 줄을 차례로 읽어 첫 오류에서 멈추고, 그다음 전선을 검증합니다.135
export function 회로만들기(노드줄들: Array<Array<string>>, 전선들: Array<전선>) -> Result<회로, string> {136
let mut 노드들: Array<노드> = []137
let mut 본아이디들: Set<string> = Set.of()138
for 토큰들 in 노드줄들 {139
let 노드값 = 노드줄파싱(토큰들)?140
if 노드값.아이디 in 본아이디들 { return Err("중복된 노드 아이디입니다: " + 노드값.아이디) }141
본아이디들.add(노드값.아이디)142
노드들.push(노드값)143
}144
전선검증(노드들, 전선들)145
}SHA256 62a5864acc5e2e38fd5380a17229e7526f72159442eef793e21376d7cb4a1511