Initial commit
This commit is contained in:
commit
e5d0963c5f
13 changed files with 10513 additions and 0 deletions
23
.github/workflows/test.yml
vendored
Normal file
23
.github/workflows/test.yml
vendored
Normal 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
4
.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
*.beam
|
||||||
|
*.ez
|
||||||
|
/build
|
||||||
|
erl_crash.dump
|
24
README.md
Normal file
24
README.md
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
# kicad_sexpr
|
||||||
|
|
||||||
|
[](https://hex.pm/packages/kicad_sexpr)
|
||||||
|
[](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
21
gleam.toml
Normal 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
16
manifest.toml
Normal 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
95
src/kicad_sexpr.gleam
Normal 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
|
||||||
|
}
|
263
src/kicad_sexpr/decode.gleam
Normal file
263
src/kicad_sexpr/decode.gleam
Normal 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
217
src/kicad_sexpr/parse.gleam
Normal 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
2947
src/kicad_sexpr/token.gleam
Normal file
File diff suppressed because it is too large
Load diff
152
test.kicad_mod
Normal file
152
test.kicad_mod
Normal 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)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
13
test/kicad_sexpr_test.gleam
Normal file
13
test/kicad_sexpr_test.gleam
Normal 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
338
test2.kicad_mod
Normal 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
6400
test3.kicad_mod
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue