Initial commit

This commit is contained in:
Lily Rose 2025-07-23 12:29:02 +10:00
commit e5d0963c5f
13 changed files with 10513 additions and 0 deletions

23
.github/workflows/test.yml vendored Normal file
View file

@ -0,0 +1,23 @@
name: test
on:
push:
branches:
- master
- main
pull_request:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: erlef/setup-beam@v1
with:
otp-version: "27.1.2"
gleam-version: "1.11.1"
rebar3-version: "3"
# elixir-version: "1"
- run: gleam deps download
- run: gleam test
- run: gleam format --check src test

4
.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
*.beam
*.ez
/build
erl_crash.dump

24
README.md Normal file
View file

@ -0,0 +1,24 @@
# kicad_sexpr
[![Package Version](https://img.shields.io/hexpm/v/kicad_sexpr)](https://hex.pm/packages/kicad_sexpr)
[![Hex Docs](https://img.shields.io/badge/hex-docs-ffaff3)](https://hexdocs.pm/kicad_sexpr/)
```sh
gleam add kicad_sexpr@1
```
```gleam
import kicad_sexpr
pub fn main() -> Nil {
// TODO: An example of the project in use
}
```
Further documentation can be found at <https://hexdocs.pm/kicad_sexpr>.
## Development
```sh
gleam run # Run the project
gleam test # Run the tests
```

21
gleam.toml Normal file
View file

@ -0,0 +1,21 @@
name = "kicad_sexpr"
version = "1.0.0"
# Fill out these fields if you intend to generate HTML documentation or publish
# your project to the Hex package manager.
#
# description = ""
# licences = ["Apache-2.0"]
# repository = { type = "github", user = "", repo = "" }
# links = [{ title = "Website", href = "" }]
#
# For a full reference of all the available options, you can have a look at
# https://gleam.run/writing-gleam/gleam-toml/.
[dependencies]
gleam_stdlib = ">= 0.44.0 and < 2.0.0"
simplifile = ">= 2.3.0 and < 3.0.0"
splitter = ">= 1.0.0 and < 2.0.0"
[dev-dependencies]
gleeunit = ">= 1.0.0 and < 2.0.0"

16
manifest.toml Normal file
View file

@ -0,0 +1,16 @@
# This file was generated by Gleam
# You typically do not need to edit this file
packages = [
{ name = "filepath", version = "1.1.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "filepath", source = "hex", outer_checksum = "B06A9AF0BF10E51401D64B98E4B627F1D2E48C154967DA7AF4D0914780A6D40A" },
{ name = "gleam_stdlib", version = "0.62.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "DC8872BC0B8550F6E22F0F698CFE7F1E4BDA7312FDEB40D6C3F44C5B706C8310" },
{ name = "gleeunit", version = "1.6.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "63022D81C12C17B7F1A60E029964E830A4CBD846BBC6740004FC1F1031AE0326" },
{ name = "simplifile", version = "2.3.0", build_tools = ["gleam"], requirements = ["filepath", "gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "0A868DAC6063D9E983477981839810DC2E553285AB4588B87E3E9C96A7FB4CB4" },
{ name = "splitter", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "splitter", source = "hex", outer_checksum = "128FC521EE33B0012E3E64D5B55168586BC1B9C8D7B0D0CA223B68B0D770A547" },
]
[requirements]
gleam_stdlib = { version = ">= 0.44.0 and < 2.0.0" }
gleeunit = { version = ">= 1.0.0 and < 2.0.0" }
simplifile = { version = ">= 2.3.0 and < 3.0.0" }
splitter = { version = ">= 1.0.0 and < 2.0.0" }

95
src/kicad_sexpr.gleam Normal file
View file

@ -0,0 +1,95 @@
import gleam/float
import gleam/int
import gleam/io
import gleam/list
import gleam/result
import gleam/string
import kicad_sexpr/decode
import kicad_sexpr/parse
import kicad_sexpr/token
import simplifile
fn print_stats(title: String, successes: Int, total: Int) {
io.println(
title
<> ": "
<> int.to_string(successes)
<> "/"
<> int.to_string(total)
<> " ("
<> int.to_float(100 * successes) /. int.to_float(total)
|> float.to_precision(2)
|> float.to_string
|> string.append("%")
<> ")",
)
}
pub fn main() -> Nil {
let assert Ok(file_names) =
simplifile.get_files("/usr/share/kicad/footprints")
// let #(file_names, _) = file_names |> list.drop(0) |> list.split(1000)
// let file_names = list.sample(file_names, 1000)
// let file_names = ["/usr/share/kicad/symbols/RF_Module.kicad_sym"]
let num_file_names = list.length(file_names)
io.println("Total: " <> int.to_string(num_file_names))
let #(successfully_read, _failed_to_read) =
file_names
|> list.map(fn(file_name) {
simplifile.read(file_name)
|> result.map(fn(res) { #(file_name, res) })
|> result.map_error(fn(res) { #(file_name, res) })
})
|> result.partition
let num_successfully_read = list.length(successfully_read)
print_stats("Read", num_successfully_read, num_file_names)
let #(successfully_parsed, _failed_to_parse) =
successfully_read
|> list.map(fn(data) {
let #(file_name, file_contents) = data
parse.run(file_contents)
|> result.map(fn(res) { #(file_name, file_contents, res) })
|> result.map_error(fn(res) { #(file_name, file_contents, res) })
})
|> result.partition
let num_successfully_parsed = list.length(successfully_parsed)
print_stats("Parsed", num_successfully_parsed, num_successfully_read)
let #(successfully_decoded, failed_to_decode) =
successfully_parsed
|> list.map(fn(data) {
let #(file_name, file_contents, sexpr) = data
decode.run(token.footprint_file, sexpr)
|> result.map(fn(res) { #(file_name, file_contents, sexpr, res) })
|> result.map_error(fn(res) { #(file_name, file_contents, sexpr, res) })
})
|> result.partition
let num_successfully_decoded = list.length(successfully_decoded)
print_stats("Decoded", num_successfully_decoded, num_successfully_parsed)
list.each(failed_to_decode, fn(data) {
let #(file_name, _file_contents, _sexpr, error) = data
io.println(file_name)
echo error
// panic
})
// let assert Ok(footprint_str) =
// simplifile.read(
// "/usr/share/kicad/footprints/Relay_SMD.pretty/Relay_DPDT_AXICOM_IMSeries_JLeg.kicad_mod",
// )
// // let assert Ok(footprint_str) = simplifile.read("test2.kicad_mod")
// let assert Ok(t) =
// parse.run(
// " (wobble \"hi there \\\" \\\\ this is a test\" (wibble foo bar) bar) ",
// )
// io.println(parse.sexpr_to_pretty_string(t))
// let assert Ok(t) = decode.run(wobble, t)
// echo t
Nil
}

View file

@ -0,0 +1,263 @@
import gleam/bool
import gleam/float
import gleam/int
import gleam/list
import gleam/option.{type Option, None, Some}
import gleam/pair
import gleam/result
import kicad_sexpr/parse.{type SExpr}
pub type ExprType {
Token(name: String)
String
Int
Float
Name
Unknown
}
pub type DecodeError {
UnexpectedEndOfAttributes(expected: ExprType)
UnexpectedTrailingAttributes(got: List(SExpr))
IncorrectExprType(got: SExpr, expected: ExprType)
IncorrectTokenName(got: String, expected: String)
IncorrectName(got: String, expected: String)
UnmatchedEnum(got: String, expected: List(String))
UnmatchedIntEnum(got: Int, expected: List(Int))
UnmatchedOneOf(got: SExpr)
TokenAttributeError(name: String, error: DecodeError)
}
pub type Decoded(a) =
Result(#(a, List(SExpr)), DecodeError)
pub type Decoder(a) =
fn(List(SExpr)) -> Decoded(a)
pub type NextFn(a, b) =
fn(a) -> Decoder(b)
pub fn token(
named name: String,
with decoder: Decoder(a),
then next: NextFn(a, b),
) -> Decoder(b) {
fn(sexprs: List(SExpr)) {
case sexprs {
[] -> Error(UnexpectedEndOfAttributes(Token(name)))
[parse.Token(name: name_, attributes:), ..sexprs] if name_ == name -> {
use #(value, rest) <- result.try(
decoder(attributes)
|> result.map_error(TokenAttributeError(name:, error: _)),
)
use <- bool.guard(
rest != [],
Error(TokenAttributeError(
name:,
error: UnexpectedTrailingAttributes(rest),
)),
)
next(value)(sexprs)
}
[parse.Token(name: name_, ..), ..] ->
Error(IncorrectTokenName(got: name_, expected: name))
[sexpr, ..] -> Error(IncorrectExprType(got: sexpr, expected: Token(name)))
}
}
}
pub fn token_wrapper(
named name: String,
with attr: fn(NextFn(a, a)) -> Decoder(b),
then next: NextFn(b, c),
) -> Decoder(c) {
token(named: name, then: next, with: attr(success))
}
pub fn string(then next: NextFn(String, a)) -> Decoder(a) {
fn(sexprs: List(SExpr)) {
case sexprs {
[] -> Error(UnexpectedEndOfAttributes(String))
[parse.String(value), ..sexprs] -> next(value)(sexprs)
[sexpr, ..] -> Error(IncorrectExprType(got: sexpr, expected: String))
}
}
}
pub fn float(then next: NextFn(Float, a)) -> Decoder(a) {
fn(sexprs: List(SExpr)) {
case sexprs {
[] -> Error(UnexpectedEndOfAttributes(Float))
[parse.Int(value), ..sexprs] -> next(int.to_float(value))(sexprs)
[parse.Float(value), ..sexprs] -> next(value)(sexprs)
[sexpr, ..] -> Error(IncorrectExprType(got: sexpr, expected: Float))
}
}
}
pub fn int(then next: NextFn(Int, a)) -> Decoder(a) {
fn(sexprs: List(SExpr)) {
case sexprs {
[] -> Error(UnexpectedEndOfAttributes(Int))
[parse.Float(value), ..sexprs] -> next(float.round(value))(sexprs)
[parse.Int(value), ..sexprs] -> next(value)(sexprs)
[sexpr, ..] -> Error(IncorrectExprType(got: sexpr, expected: Int))
}
}
}
pub fn enum(
with variants: List(#(String, a)),
then next: NextFn(a, b),
) -> Decoder(b) {
fn(sexprs: List(SExpr)) {
case sexprs {
[] -> Error(UnexpectedEndOfAttributes(Name))
[parse.Name(value), ..sexprs] ->
case list.key_find(variants, value) {
Ok(value) -> next(value)(sexprs)
Error(Nil) ->
Error(UnmatchedEnum(
got: value,
expected: list.map(variants, pair.first),
))
}
[sexpr, ..] -> Error(IncorrectExprType(got: sexpr, expected: Name))
}
}
}
pub fn int_enum(with variants: List(a), then next: NextFn(a, b)) -> Decoder(b) {
fn(sexprs: List(SExpr)) {
case sexprs {
[] -> Error(UnexpectedEndOfAttributes(Int))
[parse.Int(value), ..sexprs] -> {
let variants = list.index_map(variants, fn(x, i) { #(i, x) })
case list.key_find(variants, value) {
Ok(value) -> next(value)(sexprs)
Error(Nil) ->
Error(UnmatchedIntEnum(
got: value,
expected: list.map(variants, pair.first),
))
}
}
[sexpr, ..] -> Error(IncorrectExprType(got: sexpr, expected: Name))
}
}
}
pub fn flag(named name: String, then next: NextFn(Bool, a)) -> Decoder(a) {
fn(sexprs: List(SExpr)) {
case sexprs {
[parse.Name(value), ..sexprs] if value == name -> next(True)(sexprs)
_ -> next(False)(sexprs)
}
}
}
pub fn optional(
attr: fn(NextFn(a, a)) -> Decoder(b),
then next: NextFn(Option(b), c),
) -> Decoder(c) {
fn(source: List(SExpr)) {
case attr(success)(source) {
Ok(#(value, rest)) -> next(Some(value))(rest)
Error(TokenAttributeError(..) as err) -> Error(err)
Error(_) -> next(None)(source)
}
}
}
pub fn list(
attr: fn(NextFn(a, a)) -> Decoder(b),
then next: NextFn(List(b), c),
) -> Decoder(c) {
fn(source: List(SExpr)) {
use #(value, rest) <- result.try(do_list(source, attr(success), []))
next(value)(rest)
}
}
fn do_list(
source: List(SExpr),
decoder: Decoder(a),
results: List(a),
) -> Decoded(List(a)) {
case decoder(source) {
Ok(#(value, rest)) -> do_list(rest, decoder, [value, ..results])
Error(TokenAttributeError(..) as err) -> Error(err)
Error(_) -> Ok(#(results, source))
}
}
pub fn of(with decoder: Decoder(a), then next: NextFn(a, b)) -> Decoder(b) {
fn(source: List(SExpr)) {
use #(token, rest) <- result.try(decoder(source))
next(token)(rest)
}
}
pub fn one_of(
attr: fn(NextFn(a, a)) -> Decoder(b),
or attrs: List(fn(NextFn(a, a)) -> Decoder(b)),
then next: NextFn(b, c),
) -> Decoder(c) {
fn(source: List(SExpr)) {
case attr(success)(source) {
Ok(#(value, rest)) -> next(value)(rest)
Error(TokenAttributeError(..) as err) -> Error(err)
Error(_) -> {
use #(value, rest) <- result.try(do_one_of(
source,
list.map(attrs, fn(attr) { attr(success) }),
))
next(value)(rest)
}
}
}
}
fn do_one_of(source: List(SExpr), decoders: List(Decoder(a))) -> Decoded(a) {
case decoders {
[] ->
Error(
list.first(source)
|> result.map(UnmatchedOneOf)
|> result.unwrap(UnexpectedEndOfAttributes(Unknown)),
)
[decoder, ..decoders] ->
case decoder(source) {
Ok(#(value, rest)) -> Ok(#(value, rest))
Error(TokenAttributeError(..) as err) -> Error(err)
Error(_) -> do_one_of(source, decoders)
}
}
}
pub fn map(
with attr: fn(NextFn(a, b)) -> Decoder(c),
to map_fn: fn(a) -> b,
) -> fn(NextFn(c, d)) -> Decoder(d) {
of(then: _, with: {
use value <- attr()
success(map_fn(value))
})
}
pub fn success(value: a) -> Decoder(a) {
fn(source: List(SExpr)) { Ok(#(value, source)) }
}
pub fn failure(error: DecodeError) -> Decoder(a) {
fn(_: List(SExpr)) { Error(error) }
}
pub fn run(
structurer: fn(NextFn(a, a)) -> Decoder(b),
on source: SExpr,
) -> Result(b, DecodeError) {
use #(value, rest) <- result.try(structurer(success)([source]))
use <- bool.guard(rest != [], Error(UnexpectedTrailingAttributes(rest)))
Ok(value)
}

217
src/kicad_sexpr/parse.gleam Normal file
View file

@ -0,0 +1,217 @@
import gleam/bool
import gleam/float
import gleam/int
import gleam/list
import gleam/option.{type Option, None, Some}
import gleam/result
import gleam/string
pub type ParseError {
UnexpectedEndOfFile
UnexpectedTokenCharacter(got: String, expected: String)
UnexpectedNameCharacter(got: String)
UnexpectedNumberCharacter(got: String)
UnexpectedTrailingString(got: String)
UnterminatedString(got: String)
InvalidNumber(got: String)
}
pub type SExpr {
Token(name: String, attributes: List(SExpr))
String(String)
Int(Int)
Float(Float)
Name(String)
}
pub type Parsed(a) =
Result(#(a, String), ParseError)
pub fn sexpr_to_pretty_string(sexpr: SExpr) -> String {
do_sexpr_to_pretty_string(sexpr, "")
}
fn do_sexpr_to_pretty_string(sexpr: SExpr, pad: String) -> String {
pad
<> case sexpr {
Token(name:, attributes:) ->
name
<> " :: Token\n"
<> attributes
|> list.map(do_sexpr_to_pretty_string(_, pad <> " "))
|> string.join("\n")
String(value) -> "\"" <> value <> "\" :: String"
Int(value) -> int.to_string(value) <> " :: Int"
Float(value) -> float.to_string(value) <> " :: Float"
Name(value) -> value <> " :: Name"
}
}
pub fn run(source: String) -> Result(SExpr, ParseError) {
let source = string.trim(source)
use #(token, rest) <- result.try(attribute(source))
case string.trim(rest) {
"" -> Ok(token)
rest -> Error(UnexpectedTrailingString(rest))
}
}
pub fn token(source: String) -> Parsed(SExpr) {
use #(name, rest) <- result.try(name(source))
use #(attributes, rest) <- result.try(attributes(rest))
Ok(#(Token(name:, attributes:), rest))
}
fn name_char(source: String) -> Parsed(String) {
case source {
"" -> Error(UnexpectedEndOfFile)
"a" <> rest -> Ok(#("a", rest))
"b" <> rest -> Ok(#("b", rest))
"c" <> rest -> Ok(#("c", rest))
"d" <> rest -> Ok(#("d", rest))
"e" <> rest -> Ok(#("e", rest))
"f" <> rest -> Ok(#("f", rest))
"g" <> rest -> Ok(#("g", rest))
"h" <> rest -> Ok(#("h", rest))
"i" <> rest -> Ok(#("i", rest))
"j" <> rest -> Ok(#("j", rest))
"k" <> rest -> Ok(#("k", rest))
"l" <> rest -> Ok(#("l", rest))
"m" <> rest -> Ok(#("m", rest))
"n" <> rest -> Ok(#("n", rest))
"o" <> rest -> Ok(#("o", rest))
"p" <> rest -> Ok(#("p", rest))
"q" <> rest -> Ok(#("q", rest))
"r" <> rest -> Ok(#("r", rest))
"s" <> rest -> Ok(#("s", rest))
"t" <> rest -> Ok(#("t", rest))
"u" <> rest -> Ok(#("u", rest))
"v" <> rest -> Ok(#("v", rest))
"w" <> rest -> Ok(#("w", rest))
"x" <> rest -> Ok(#("x", rest))
"y" <> rest -> Ok(#("y", rest))
"z" <> rest -> Ok(#("z", rest))
"_" <> rest -> Ok(#("_", rest))
_ ->
Error(UnexpectedNameCharacter(string.first(source) |> result.unwrap("")))
}
}
pub fn name(source: String) -> Parsed(String) {
use #(char, rest) <- result.try(name_char(source))
do_name(rest, char)
}
fn do_name(source: String, result: String) -> Parsed(String) {
case name_char(source) {
Ok(#(char, rest)) -> do_name(rest, result <> char)
Error(_) -> Ok(#(result, source))
}
}
pub fn attributes(source: String) -> Parsed(List(SExpr)) {
do_attributes(source, [])
}
fn do_attributes(source: String, attributes: List(SExpr)) -> Parsed(List(SExpr)) {
case string.trim_start(source) {
"" -> Error(UnexpectedEndOfFile)
")" <> rest -> Ok(#(list.reverse(attributes), rest))
source -> {
use #(attribute, rest) <- result.try(attribute(source))
do_attributes(rest, [attribute, ..attributes])
}
}
}
pub fn attribute(source: String) -> Parsed(SExpr) {
case source {
"" -> Error(UnexpectedEndOfFile)
"(" <> rest -> {
use #(token, rest) <- result.try(token(rest))
Ok(#(token, rest))
}
"\"" <> rest -> {
use #(str, rest) <- result.try(string(rest))
Ok(#(String(str), rest))
}
source -> {
use <- option.lazy_unwrap(try_number(source))
use #(name, rest) <- result.try(name(source))
Ok(#(Name(name), rest))
}
}
}
pub fn string(source: String) -> Parsed(String) {
use <- bool.guard(source == "", Error(UnexpectedEndOfFile))
do_string(source, "")
}
fn do_string(source: String, result: String) -> Parsed(String) {
case string.split_once(source, "\"") {
Ok(#(start, rest)) ->
case string.last(start) {
Ok("\\") -> do_string(rest, result <> "\"" <> start)
_ ->
Ok(#(
result <> start,
// |> string.replace("\\n", "\n")
// |> string.replace("\\r", "\r")
// |> string.replace("\\t", "\t")
// |> string.replace("\\f", "\f")
rest,
))
}
Error(Nil) -> Error(UnterminatedString(source))
}
}
fn number_char(source: String) -> Parsed(String) {
case source {
"" -> Error(UnexpectedEndOfFile)
"." <> rest -> Ok(#(".", rest))
"-" <> rest -> Ok(#("-", rest))
"0" <> rest -> Ok(#("0", rest))
"1" <> rest -> Ok(#("1", rest))
"2" <> rest -> Ok(#("2", rest))
"3" <> rest -> Ok(#("3", rest))
"4" <> rest -> Ok(#("4", rest))
"5" <> rest -> Ok(#("5", rest))
"6" <> rest -> Ok(#("6", rest))
"7" <> rest -> Ok(#("7", rest))
"8" <> rest -> Ok(#("8", rest))
"9" <> rest -> Ok(#("9", rest))
_ ->
Error(UnexpectedNumberCharacter(string.first(source) |> result.unwrap("")))
}
}
pub fn try_number(source: String) -> Option(Parsed(SExpr)) {
case number_char(source) {
Ok(#(char, rest)) -> Some(do_number(rest, char))
Error(_) -> None
}
}
fn do_number(source: String, result: String) -> Parsed(SExpr) {
case number_char(source) {
Ok(#(char, rest)) -> do_number(rest, result <> char)
Error(_) -> {
case int.parse(result) {
Ok(n) -> Ok(#(Int(n), source))
Error(Nil) -> {
let result = case result {
"-." <> rest -> "-0." <> rest
"." <> rest -> "0." <> rest
result -> result
}
case float.parse(result) {
Ok(n) -> Ok(#(Float(n), source))
Error(Nil) -> Error(InvalidNumber(result))
}
}
}
}
}
}

2947
src/kicad_sexpr/token.gleam Normal file

File diff suppressed because it is too large Load diff

152
test.kicad_mod Normal file
View file

@ -0,0 +1,152 @@
(footprint "R_1812_4532Metric"
(version 20241229)
(generator "kicad-footprint-generator")
(layer "F.Cu")
(descr "Resistor SMD 1812 (4532 Metric), square (rectangular) end terminal, IPC-7351 nominal, (Body size source: https://www.nikhef.nl/pub/departments/mt/projects/detectorR_D/dtddice/ERJ2G.pdf), generated with kicad-footprint-generator")
(tags "resistor")
(property "Reference" "REF**"
(at 0 -2.65 0)
(layer "F.SilkS")
(effects
(font
(size 1 1)
(thickness 0.15)
)
)
)
(property "Value" "R_1812_4532Metric"
(at 0 2.65 0)
(layer "F.Fab")
(effects
(font
(size 1 1)
(thickness 0.15)
)
)
)
(attr smd)
(fp_line
(start -1.386252 -1.71)
(end 1.386252 -1.71)
(stroke
(width 0.12)
(type solid)
)
(layer "F.SilkS")
)
(fp_line
(start -1.386252 1.71)
(end 1.386252 1.71)
(stroke
(width 0.12)
(type solid)
)
(layer "F.SilkS")
)
(fp_line
(start -2.95 -1.95)
(end 2.95 -1.95)
(stroke
(width 0.05)
(type solid)
)
(layer "F.CrtYd")
)
(fp_line
(start -2.95 1.95)
(end -2.95 -1.95)
(stroke
(width 0.05)
(type solid)
)
(layer "F.CrtYd")
)
(fp_line
(start 2.95 -1.95)
(end 2.95 1.95)
(stroke
(width 0.05)
(type solid)
)
(layer "F.CrtYd")
)
(fp_line
(start 2.95 1.95)
(end -2.95 1.95)
(stroke
(width 0.05)
(type solid)
)
(layer "F.CrtYd")
)
(fp_line
(start -2.25 -1.6)
(end 2.25 -1.6)
(stroke
(width 0.1)
(type solid)
)
(layer "F.Fab")
)
(fp_line
(start -2.25 1.6)
(end -2.25 -1.6)
(stroke
(width 0.1)
(type solid)
)
(layer "F.Fab")
)
(fp_line
(start 2.25 -1.6)
(end 2.25 1.6)
(stroke
(width 0.1)
(type solid)
)
(layer "F.Fab")
)
(fp_line
(start 2.25 1.6)
(end -2.25 1.6)
(stroke
(width 0.1)
(type solid)
)
(layer "F.Fab")
)
(fp_text user "${REFERENCE}"
(at 0 0 0)
(layer "F.Fab")
(effects
(font
(size 1 1)
(thickness 0.15)
)
)
)
(pad "1" smd roundrect
(at -2.1375 0)
(size 1.125 3.4)
(layers "F.Cu" "F.Mask" "F.Paste")
(roundrect_rratio 0.222222)
)
(pad "2" smd roundrect
(at 2.1375 0)
(size 1.125 3.4)
(layers "F.Cu" "F.Mask" "F.Paste")
(roundrect_rratio 0.222222)
)
(embedded_fonts no)
(model "${KICAD9_3DMODEL_DIR}/Resistor_SMD.3dshapes/R_1812_4532Metric.step"
(offset
(xyz 0 0 0)
)
(scale
(xyz 1 1 1)
)
(rotate
(xyz 0 0 0)
)
)
)

View file

@ -0,0 +1,13 @@
import gleeunit
pub fn main() -> Nil {
gleeunit.main()
}
// gleeunit test functions end in `_test`
pub fn hello_world_test() {
let name = "Joe"
let greeting = "Hello, " <> name <> "!"
assert greeting == "Hello, Joe!"
}

338
test2.kicad_mod Normal file
View file

@ -0,0 +1,338 @@
(footprint "Relay_DPDT_AXICOM_IMSeries_JLeg"
(version 20241229)
(generator "pcbnew")
(generator_version "9.0")
(layer "F.Cu")
(descr "http://www.te.com/commerce/DocumentDelivery/DDEController?Action=showdoc&DocId=Specification+Or+Standard%7F108-98001%7FW5%7Fpdf%7FEnglish%7FENG_SS_108-98001_W5.pdf")
(tags "AXICOM IM-Series Relay J JLeg")
(property "Reference" "REF**"
(at 0 -6 0)
(layer "F.SilkS")
(uuid "dd99cfbd-d6b2-4ffd-9f36-eff1e9c4d053")
(effects
(font
(size 1 1)
(thickness 0.15)
)
)
)
(property "Value" "Relay_DPDT_AXICOM_IMSeries_JLeg"
(at 0 6.25 0)
(layer "F.Fab")
(uuid "9aa3f47c-195e-4a1e-a0ef-27453eee41fe")
(effects
(font
(size 1 1)
(thickness 0.15)
)
)
)
(property "Datasheet" ""
(at 0 0 0)
(unlocked yes)
(layer "F.Fab")
(hide yes)
(uuid "9a83d93b-caf9-4598-9f8a-6d35d6cad0f8")
(effects
(font
(size 1.27 1.27)
(thickness 0.15)
)
)
)
(property "Description" ""
(at 0 0 0)
(unlocked yes)
(layer "F.Fab")
(hide yes)
(uuid "46230326-fc25-48f7-99b1-be130eacca1a")
(effects
(font
(size 1.27 1.27)
(thickness 0.15)
)
)
)
(attr smd)
(fp_line
(start -3.2 -5.2)
(end -3.2 -4.45)
(stroke
(width 0.15)
(type solid)
)
(layer "F.SilkS")
(uuid "67f237fb-d063-4749-ad1c-21422dc150de")
)
(fp_line
(start -3.2 4.45)
(end -3.2 5.2)
(stroke
(width 0.15)
(type solid)
)
(layer "F.SilkS")
(uuid "9c445b8f-71dc-4f39-8924-f59f9346b135")
)
(fp_line
(start -3.2 5.2)
(end -2.45 5.2)
(stroke
(width 0.15)
(type solid)
)
(layer "F.SilkS")
(uuid "6b60da81-bb01-4777-9085-4e5296c89d91")
)
(fp_line
(start -3.05 -5.2)
(end -3.2 -5.05)
(stroke
(width 0.15)
(type solid)
)
(layer "F.SilkS")
(uuid "0dd8d971-8a43-4797-840a-0380dabdb52e")
)
(fp_line
(start -2.9 -5.2)
(end -3.2 -4.9)
(stroke
(width 0.15)
(type solid)
)
(layer "F.SilkS")
(uuid "3ab4bec7-3ca0-40d3-8118-cf643eb51c1f")
)
(fp_line
(start -2.75 -5.2)
(end -3.2 -4.75)
(stroke
(width 0.15)
(type solid)
)
(layer "F.SilkS")
(uuid "6867aae2-5d0a-41ca-b124-cbd3047e8be2")
)
(fp_line
(start -2.6 -5.2)
(end -3.2 -4.6)
(stroke
(width 0.15)
(type solid)
)
(layer "F.SilkS")
(uuid "e4e2b51b-7f54-4d83-b7e2-0c837bc417c9")
)
(fp_line
(start -2.45 -5.2)
(end -3.2 -5.2)
(stroke
(width 0.15)
(type solid)
)
(layer "F.SilkS")
(uuid "d1c6f2c7-f2a5-40f5-9ef5-d1284a7a4b04")
)
(fp_line
(start 2.45 -5.2)
(end 3.2 -5.2)
(stroke
(width 0.15)
(type solid)
)
(layer "F.SilkS")
(uuid "ed1bd233-1f89-4a37-a9ec-64ec98ba8504")
)
(fp_line
(start 2.45 5.2)
(end 3.2 5.2)
(stroke
(width 0.15)
(type solid)
)
(layer "F.SilkS")
(uuid "2e76801f-1cb5-4afa-9818-db56f6fd6b4b")
)
(fp_line
(start 3.2 -5.2)
(end 3.2 -4.45)
(stroke
(width 0.15)
(type solid)
)
(layer "F.SilkS")
(uuid "aec04f79-dd80-41be-aae9-fd4c7e25e0eb")
)
(fp_line
(start 3.2 4.45)
(end 3.2 5.2)
(stroke
(width 0.15)
(type solid)
)
(layer "F.SilkS")
(uuid "89c6d544-8ee2-46dd-a009-96bbeb63f108")
)
(fp_line
(start -3.45 -5.25)
(end -3.45 5.25)
(stroke
(width 0.05)
(type solid)
)
(layer "F.CrtYd")
(uuid "577bb927-56fc-4aa5-a4f2-056a46d08910")
)
(fp_line
(start -3.45 5.25)
(end 3.45 5.25)
(stroke
(width 0.05)
(type solid)
)
(layer "F.CrtYd")
(uuid "4a3c332a-be3b-47ba-90d7-fa9675b4fdd0")
)
(fp_line
(start 3.45 -5.25)
(end -3.45 -5.25)
(stroke
(width 0.05)
(type solid)
)
(layer "F.CrtYd")
(uuid "6ded1472-d1d9-4904-b808-597851b7a26f")
)
(fp_line
(start 3.45 5.25)
(end 3.45 -5.25)
(stroke
(width 0.05)
(type solid)
)
(layer "F.CrtYd")
(uuid "6c3f1e97-16d3-404a-ac59-209747f4039e")
)
(fp_line
(start -3 -4.5)
(end -2.5 -5)
(stroke
(width 0.15)
(type solid)
)
(layer "F.Fab")
(uuid "9359c75d-e786-4ae7-a81f-0a706b450a81")
)
(fp_line
(start -3 5)
(end -3 -4.5)
(stroke
(width 0.15)
(type solid)
)
(layer "F.Fab")
(uuid "da412642-80db-4449-8b83-730ed4a510c0")
)
(fp_line
(start -2.5 -5)
(end 3 -5)
(stroke
(width 0.15)
(type solid)
)
(layer "F.Fab")
(uuid "d8faf5d8-35c5-48ab-8bd9-a78a780756e0")
)
(fp_line
(start 3 -5)
(end 3 5)
(stroke
(width 0.15)
(type solid)
)
(layer "F.Fab")
(uuid "1e570dff-540f-48e6-b8c4-080ac111214a")
)
(fp_line
(start 3 5)
(end -3 5)
(stroke
(width 0.15)
(type solid)
)
(layer "F.Fab")
(uuid "19922db0-8619-4c33-bf9c-63ee863f246a")
)
(fp_text user "${REFERENCE}"
(at 0.06 0 90)
(layer "F.Fab")
(uuid "6b26c96a-b82c-4db6-85ae-d9cb09a6f67b")
(effects
(font
(size 1 1)
(thickness 0.15)
)
)
)
(pad "1" smd rect
(at -2.2 -3.8)
(size 2 0.7)
(layers "F.Cu" "F.Mask" "F.Paste")
(uuid "82eff78a-635a-433b-b1ee-de6410e81430")
)
(pad "2" smd rect
(at -2.2 -0.6)
(size 2 0.7)
(layers "F.Cu" "F.Mask" "F.Paste")
(uuid "05985d65-1bc3-4618-a5d8-cafdc1a66970")
)
(pad "3" smd rect
(at -2.2 1.6)
(size 2 0.7)
(layers "F.Cu" "F.Mask" "F.Paste")
(uuid "d8966a22-f301-465e-882c-18cafa507d67")
)
(pad "4" smd rect
(at -2.2 3.8)
(size 2 0.7)
(layers "F.Cu" "F.Mask" "F.Paste")
(uuid "a0d6dc62-a560-4289-9c84-1a25bb0be83e")
)
(pad "5" smd rect
(at 2.2 3.8)
(size 2 0.7)
(layers "F.Cu" "F.Mask" "F.Paste")
(uuid "c489d137-cea0-4d95-a34c-4e8dc5daa9d4")
)
(pad "6" smd rect
(at 2.2 1.6)
(size 2 0.7)
(layers "F.Cu" "F.Mask" "F.Paste")
(uuid "dafce1a4-6cb0-478b-9d93-4c7f8069698d")
)
(pad "7" smd rect
(at 2.2 -0.6)
(size 2 0.7)
(layers "F.Cu" "F.Mask" "F.Paste")
(uuid "255cb5c5-374a-41a6-8f55-ae69caccfcf7")
)
(pad "8" smd rect
(at 2.2 -3.8)
(size 2 0.7)
(layers "F.Cu" "F.Mask" "F.Paste")
(uuid "c637267d-9a42-4816-9c0e-4c9e3c7278c1")
)
(embedded_fonts no)
(model "${KICAD9_3DMODEL_DIR}/Relay_SMD.3dshapes/Relay_DPDT_AXICOM_IMSeries_JLeg.step"
(offset
(xyz 0 0 0)
)
(scale
(xyz 1 1 1)
)
(rotate
(xyz 0 0 0)
)
)
)

6400
test3.kicad_mod Normal file

File diff suppressed because it is too large Load diff