Skip to content

Commit

Permalink
feat: add type search
Browse files Browse the repository at this point in the history
  • Loading branch information
ghivert committed Jul 6, 2024
1 parent 8dd1bcc commit c37df85
Show file tree
Hide file tree
Showing 17 changed files with 290 additions and 47 deletions.
15 changes: 14 additions & 1 deletion apps/backend/src/api/signatures.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,12 @@ import backend/gleam/generate/types.{
constant_to_json, function_to_json, type_alias_to_json,
type_definition_to_json,
}
import backend/gleam/type_search/state as type_search
import backend/postgres/queries
import gleam/bool
import gleam/dict
import gleam/erlang/process
import gleam/function
import gleam/json
import gleam/list
import gleam/option.{None, Some}
Expand Down Expand Up @@ -132,20 +135,30 @@ fn upsert_functions(ctx: Context, module: context.Module) {
result.all({
use #(function_name, function) <- list.map(all_functions)
use gen <- result.try(function_to_json(ctx, function_name, function))
let signature = function_to_string(function_name, function)
queries.upsert_package_type_fun_signature(
db: ctx.db,
name: function_name,
kind: queries.Function,
documentation: function.documentation,
metadata: Some(function.implementations)
|> metadata.generate(function.deprecation, _),
signature: function_to_string(function_name, function),
signature: signature,
json_signature: gen.0,
parameters: gen.1,
module_id: module.id,
deprecation: function.deprecation,
implementations: Some(function.implementations),
)
|> function.tap(fn(content) {
case ctx.type_search_subject, content {
option.Some(subject), Ok([id]) -> {
process.send(subject, type_search.Add(signature, id))
content
}
_, _ -> content
}
})
})
}

Expand Down
10 changes: 10 additions & 0 deletions apps/backend/src/backend.gleam
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import backend/config
import backend/gleam/type_search/state as type_search
import backend/postgres/postgres
import backend/router
import dot_env
Expand All @@ -25,6 +26,15 @@ pub fn main() {
logger.set_level(cnf.level)
setup.radiate()

let assert Ok(subject) = type_search.init(ctx.db)
// let assert Ok(_) =
// supervisor.start(fn(children) {
// use _ <- function.tap(children)
// supervisor.add(children, { supervisor.worker(fn(_) { Ok(subject) }) })
// })

let ctx = ctx |> config.add_type_search_subject(subject)

let assert Ok(_) =
router.handle_request(_, ctx)
|> wisp.mist_handler(secret_key_base)
Expand Down
8 changes: 8 additions & 0 deletions apps/backend/src/backend/config.gleam
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import backend/gleam/type_search/state as type_search
import gleam/erlang/os
import gleam/erlang/process.{type Subject}
import gleam/int
import gleam/option.{type Option}
import gleam/pgo
import gleam/result
import wisp
Expand All @@ -16,6 +19,7 @@ pub type Context {
hex_api_key: String,
github_token: String,
env: Environment,
type_search_subject: Option(Subject(type_search.Msg)),
)
}

Expand Down Expand Up @@ -66,3 +70,7 @@ pub fn scaleway_keys() {
let assert Ok(secret_key) = os.get_env("SCALEWAY_SECRET_KEY")
#(access_key, secret_key)
}

pub fn add_type_search_subject(context, subject) {
Context(..context, type_search_subject: option.Some(subject))
}
4 changes: 4 additions & 0 deletions apps/backend/src/backend/gleam/context.gleam
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import backend/gleam/type_search/state as type_search
import gleam/dict.{type Dict}
import gleam/erlang/process.{type Subject}
import gleam/option.{type Option}
import gleam/package_interface
import gleam/pgo
import tom
Expand All @@ -11,6 +14,7 @@ pub type Context {
/// Allow to bypass parameters relations if activated.
/// This allows to ignore internals for example.
ignore_parameters_errors: Bool,
type_search_subject: Option(Subject(type_search.Msg)),
)
}

Expand Down
77 changes: 49 additions & 28 deletions apps/backend/src/backend/gleam/parse.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import chomp.{do, return}
import chomp/lexer
import chomp/span
import gleam/dict.{type Dict}
import gleam/io
import gleam/list
import gleam/option.{None, Some}
import gleam/pair
Expand All @@ -14,23 +13,9 @@ pub type Kind {
Index(String, Int)
Custom(String, List(Kind))
Function(List(Kind), Kind)
Tuple(List(Kind))
}

pub fn main() {
"fn inner_text(plinth/browser/element.Element) -> String"
|> parse_function
|> io.debug
}

// pub type SigKind(a) {
// Kind(Kind)
// Fn(a)
// }

// pub type Signature {
// Signature(children: Dict(Kind(Signature), List(Signature)), rows: List(Int))
// }

fn parse_qualified_name() {
parse_upper_name()
|> chomp.then(fn(content) {
Expand Down Expand Up @@ -62,39 +47,67 @@ fn parse_name() {
}
}

fn parse_label() {
chomp.backtrackable({
use name <- do(
chomp.take_map(fn(token) {
case token {
token.Name(content) -> Some(Index(content, 0))
_ -> None
}
}),
)
use _ <- do(chomp.token(token.Colon))
return(name)
})
}

fn parse_type_parameter() {
use _ <- do(chomp.token(token.LeftParen))
use content <- do(
chomp.one_of([parse_qualified_name(), parse_name()])
|> chomp.sequence(chomp.token(token.Comma)),
)
use content <- do(parse_kind() |> chomp.sequence(chomp.token(token.Comma)))
use _ <- do(chomp.token(token.RightParen))
return(content)
}

fn parse_return() {
use _ <- do(chomp.token(token.RightArrow))
use content <- do(parse_upper_name())
use content <- do(parse_kind())
return(content)
}

fn parse_tuple() {
use _ <- do(chomp.token(token.Hash))
use _ <- do(chomp.token(token.LeftParen))
use content <- do(parse_kind() |> chomp.sequence(chomp.token(token.Comma)))
use _ <- do(chomp.token(token.RightParen))
return(Tuple(content))
}

fn parse_fn() {
use _ <- do(chomp.token(token.Fn))
use _ <- do(chomp.optional(parse_name()))
use _ <- do(chomp.token(token.LeftParen))
use _ <- do(chomp.optional(chomp.token(token.LeftParen)))
use content <- do(
chomp.one_of([
parse_fn(),
chomp.backtrackable(parse_qualified_name()),
parse_name(),
])
{
use _ <- do(chomp.optional(parse_label()))
parse_kind()
}
|> chomp.sequence(chomp.token(token.Comma)),
)
use _ <- do(chomp.token(token.RightParen))
use _ <- do(chomp.optional(chomp.token(token.RightParen)))
use content_ <- do(parse_return())
return(Function(content, content_))
}

fn parse_kind() {
chomp.one_of([
parse_fn(),
parse_tuple(),
chomp.backtrackable(parse_qualified_name()),
parse_name(),
])
}

pub fn parse_function(input: String) {
let tokens =
input
Expand Down Expand Up @@ -141,5 +154,13 @@ fn replace_indexed(
let #(return_value, accs) = replace_indexed(accs, return_value)
#(Function(list.reverse(new_kinds), return_value), accs)
}
Tuple(kinds) -> {
let #(new_kinds, accs) =
list.fold(kinds, #([], #(indexes, current)), fn(acc, val) {
let res = replace_indexed(acc.1, val)
#([res.0, ..acc.0], res.1)
})
#(Tuple(list.reverse(new_kinds)), accs)
}
}
}
8 changes: 7 additions & 1 deletion apps/backend/src/backend/gleam/type_search.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,13 @@ fn do_add(searches: TypeSearch, kinds: List(#(Kind, option.Option(Int)))) {
[#(kind, option.Some(id))] ->
dict.get(searches.entries, kind)
|> result.unwrap(empty())
|> fn(s) { TypeSearch(..s, rows: [id, ..s.rows]) }
|> fn(s: TypeSearch) {
let rows = case list.contains(s.rows, id) {
True -> s.rows
False -> [id, ..s.rows]
}
TypeSearch(..s, rows: rows)
}
|> dict.insert(searches.entries, kind, _)
|> fn(a) { TypeSearch(..searches, entries: a) }
[#(kind, _), ..rest] ->
Expand Down
85 changes: 85 additions & 0 deletions apps/backend/src/backend/gleam/type_search/state.gleam
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import backend/gleam/parse
import backend/gleam/type_search.{type TypeSearch}
import gleam/bool
import gleam/dynamic
import gleam/erlang/process.{type Subject}
import gleam/function
import gleam/list
import gleam/option.{type Option}
import gleam/otp/actor
import gleam/pgo
import gleam/result

pub type State {
State(db: pgo.Connection, search: TypeSearch)
}

pub type Msg {
Find(Subject(Option(List(Int))), String)
Add(String, Int)
}

pub fn init(db: pgo.Connection) {
let init = fn() {
let search =
compute_rows(0, db, #(0, type_search.empty()), {
fn(search: #(Int, TypeSearch), row: #(String, Int)) {
let #(signature, id) = row
signature
|> parse.parse_function
|> result.map(fn(kind) {
#(search.0 + 1, type_search.add(search.1, kind, id))
})
|> result.unwrap(search)
}
})
process.new_selector()
|> process.selecting(process.new_subject(), function.identity)
|> actor.Ready(State(db, search.1), _)
}
actor.start_spec(actor.Spec(init, init_timeout: 10_000, loop: loop))
}

fn loop(msg: Msg, state: State) -> actor.Next(Msg, State) {
case msg {
Find(subject, signature) -> {
signature
|> parse.parse_function
|> result.map(type_search.find(state.search, _))
|> result.unwrap(option.None)
|> function.tap(fn(res) { process.send(subject, res) })
actor.continue(state)
}
Add(signature, id) -> {
signature
|> parse.parse_function
|> result.map(fn(kind) { type_search.add(state.search, kind, id) })
|> result.unwrap(state.search)
|> fn(s) { State(..state, search: s) }
|> actor.continue
}
}
actor.continue(state)
}

fn compute_rows(
offset: Int,
db: pgo.Connection,
default: a,
next: fn(a, #(String, Int)) -> a,
) {
let decoder = dynamic.tuple2(dynamic.string, dynamic.int)
let rows =
"SELECT signature_, id
FROM package_type_fun_signature
WHERE kind = 'function'
ORDER BY id ASC
LIMIT 1000
OFFSET $1"
|> pgo.execute(db, [pgo.int(offset)], decoder)
|> result.map(fn(r) { r.rows })
|> result.unwrap([])
use <- bool.guard(when: list.is_empty(rows), return: default)
list.fold(rows, default, next)
|> compute_rows(offset + 1000, db, _, next)
}
1 change: 1 addition & 0 deletions apps/backend/src/backend/postgres/postgres.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ pub fn connect(cnf: Config) {
hex_api_key: cnf.hex_api_key,
github_token: cnf.github_token,
env: cnf.env,
type_search_subject: option.None,
)
}
}
Expand Down
Loading

0 comments on commit c37df85

Please sign in to comment.