// Parser: turn the line-based point-table text into a Device.
//
// Point table format (one register per line, `#` starts a comment):
//    
[] [] // Example: // coil_temp 40001 float32 R degC jsonb->coil_temp // conveyor 40003 bool RW jsonb->conveyor_run // // The Modbus area (coil / discrete input / input register / holding register) // is inferred from the address prefix, so no extra header is required. ///| /// Parse a whole point-table document. pub fn parse_device(text : String) -> Result[Device, String] { let lines = split_lines(text) let regs : Array[Register] = [] for i = 0; i < lines.length(); i = i + 1 { let raw = lines[i].trim().to_owned() if raw.length() == 0 { continue } if raw[0] == '#' { continue } let r = match parse_line(raw, i + 1) { Ok(v) => v Err(e) => return Err(e) } regs.push(r) } Ok({ registers: regs, }) } ///| /// Parse a single register line. fn parse_line(line : String, line_no : Int) -> Result[Register, String] { let toks = split_ws(line) if toks.length() < 4 { return Err( "line " + line_no.to_string() + ": expected ' [unit] [jsonb]', got " + toks.length().to_string() + " field(s)", ) } let name = toks[0] let addr = match parse_int(toks[1]) { Ok(v) => v Err(e) => return Err("line " + line_no.to_string() + ": bad address: " + e) } let rtype = match parse_type(toks[2]) { Ok(v) => v Err(e) => return Err("line " + line_no.to_string() + ": " + e) } let access = match parse_access(toks[3]) { Ok(v) => v Err(e) => return Err("line " + line_no.to_string() + ": " + e) } let unit = if toks.length() > 4 { toks[4] } else { "" } let jsonb = if toks.length() > 5 { toks[5] } else { "" } Ok({ name, address: addr, rtype, access, unit, jsonb, line: line_no, }) } ///| test "parse_device reads comments and registers" { let text = "# example point table\n" + "coil_temp 40001 float32 R degC jsonb->coil_temp\n" + "\n" + "conveyor 40003 bool RW jsonb->conveyor_run\n" let dev = parse_device(text).unwrap() assert_eq(dev.registers.length(), 2) assert_eq(dev.registers[0].name, "coil_temp") assert_eq(dev.registers[1].address, 40003) } ///| test "parse_device rejects unknown type" { let res = parse_device("x 40001 unknown R") assert_eq(res is Ok(_), false) } ///| test "parse_device rejects too-few fields" { let res = parse_device("x 40001 float32") assert_eq(res is Ok(_), false) }