Skip to content

Commit

Permalink
builtin avg
Browse files Browse the repository at this point in the history
  • Loading branch information
bcpeinhardt committed Nov 28, 2024
1 parent 66b7cb1 commit 020b07f
Show file tree
Hide file tree
Showing 10 changed files with 510 additions and 17 deletions.
371 changes: 355 additions & 16 deletions priv/static/squared_away.mjs

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion src/squared_away/squared_away_lang.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ pub fn dependency_list(
list.flatten([deps, acc])
}
typed_expr.BooleanLiteral(_, _) -> acc
typed_expr.BuiltinSum(_, keys) ->
typed_expr.BuiltinSum(_, keys) | typed_expr.BuiltinAvg(_, keys) ->
list.map(keys, fn(k) {
case grid.get(input, k) {
Error(_) -> [k]
Expand Down Expand Up @@ -91,6 +91,7 @@ pub fn parse_grid(
|> list.map(fn(t) {
case t {
token.BuiltinSum(option.None) -> token.BuiltinSum(option.Some(key))
token.BuiltinAvg(option.None) -> token.BuiltinAvg(option.Some(key))
_ -> t
}
})
Expand Down
62 changes: 62 additions & 0 deletions src/squared_away/squared_away_lang/interpreter.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -245,5 +245,67 @@ pub fn interpret(
)
}
}
typed_expr.BuiltinAvg(type_:, keys:) -> {
let values =
grid.to_list(env)
|> list.filter_map(fn(i) {
let #(gk, item) = i
case list.contains(keys, gk) {
False -> Error(Nil)
True ->
case item {
Error(_) -> Error(Nil)
Ok(x) -> {
interpret(env, x) |> result.nil_error
}
}
}
})
|> list.filter(fn(v) {
case v {
value.TestFail | value.TestPass -> False
_ -> True
}
})

case type_ {
typ.TFloat -> {
let sum =
list.map(values, fn(v) {
let assert value.FloatingPointNumber(f) = v
f
})
|> float.sum

Ok(value.FloatingPointNumber(sum /. int.to_float(list.length(values))))
}

typ.TInt -> {
let sum =
list.map(values, fn(v) {
let assert value.Integer(i) = v
i
})
|> int.sum

Ok(value.Integer(sum / list.length(values)))
}

typ.TUsd ->
list.map(values, fn(v) {
let assert value.Usd(d) = v
d
})
|> rational.avg
|> value.Usd
|> Ok
_ ->
Error(
error.RuntimeError(runtime_error.RuntimeError(
"internal compiler error sum function interpret",
)),
)
}
}
}
}
6 changes: 6 additions & 0 deletions src/squared_away/squared_away_lang/parser.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ fn do_parse(
Error(_) -> Ok(#(expr.BuiltinSum(key), rest))
}
}
[token.BuiltinAvg(key), ..rest] -> {
case try_parse_binary_ops(rest) {
Ok(#(op, rest)) -> Ok(#(op(expr.BuiltInAvg(key)), rest))
Error(_) -> Ok(#(expr.BuiltInAvg(key), rest))
}
}
[token.Label(str), ..rest] -> {
case try_parse_binary_ops(rest) {
Ok(#(op, rest)) -> Ok(#(op(expr.Label(str)), rest))
Expand Down
1 change: 1 addition & 0 deletions src/squared_away/squared_away_lang/parser/expr.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ pub type Expr {
BinaryOp(lhs: Expr, op: BinaryOpKind, rhs: Expr)
Group(inner: Expr)
BuiltinSum(key: option.Option(grid.GridKey))
BuiltInAvg(key: option.Option(grid.GridKey))
}

pub type BinaryOpKind {
Expand Down
2 changes: 2 additions & 0 deletions src/squared_away/squared_away_lang/scanner.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ fn do_scan(
"mustbe" <> rest -> do_scan(string.trim_left(rest), [token.MustBe, ..acc])
"sum" <> rest ->
do_scan(string.trim_left(rest), [token.BuiltinSum(option.None), ..acc])
"avg" <> rest ->
do_scan(string.trim_left(rest), [token.BuiltinAvg(option.None), ..acc])

// Operators
"&&" <> rest -> do_scan(string.trim_left(rest), [token.And, ..acc])
Expand Down
1 change: 1 addition & 0 deletions src/squared_away/squared_away_lang/scanner/token.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -53,5 +53,6 @@ pub type Token {
LabelDef(txt: String)
Underscore
BuiltinSum(key: option.Option(grid.GridKey))
BuiltinAvg(key: option.Option(grid.GridKey))
MustBe
}
75 changes: 75 additions & 0 deletions src/squared_away/squared_away_lang/typechecker.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,81 @@ pub fn typecheck(
)
}
}
expr.BuiltInAvg(key) -> {
let assert option.Some(key) = key

// The sum builtin will sum up all the values above it until it finds a label, so we need to fetch those
// values and sort them
let items_above_sum_call =
grid.to_list(env)
|> list.filter(fn(i) {
let #(gk, _) = i
grid.col(gk) == grid.col(key) && grid.row(gk) < grid.row(key)
})
|> list.sort(fn(i1, i2) {
let #(gk1, _) = i1
let #(gk2, _) = i2
int.compare(grid.row(gk1), grid.row(gk2))
})
|> list.reverse
|> list.take_while(fn(i) {
let #(_, item) = i
case item {
Ok(expr.LabelDef(_)) -> False
_ -> True
}
})

let keys = list.map(items_above_sum_call, fn(i) { i.0 })
let items_above_sum_call = list.map(items_above_sum_call, fn(i) { i.1 })

use <- bool.guard(
list.any(items_above_sum_call, result.is_error),
Error(
error.TypeError(type_error.TypeError(
"Cell above sum expression has error",
)),
),
)

let types =
list.map(items_above_sum_call, fn(i) {
let assert Ok(item) = i
item
})
|> list.unique
|> list.map(fn(e) { typecheck(env, e) })

use <- bool.guard(
list.any(types, result.is_error),
Error(
error.TypeError(type_error.TypeError(
"Cell above sum expression has type error",
)),
),
)

let types =
list.map(types, fn(t) {
let assert Ok(texpr) = t
texpr.type_
})
|> list.unique
|> list.filter(fn(t) { t != typ.TTestResult })

case types {
[typ.TFloat] -> Ok(typed_expr.BuiltinAvg(typ.TFloat, keys))
[typ.TInt] -> Ok(typed_expr.BuiltinAvg(typ.TInt, keys))
[typ.TUsd] -> Ok(typed_expr.BuiltinAvg(typ.TUsd, keys))
[typ.TPercent] -> Ok(typed_expr.BuiltinAvg(typ.TPercent, keys))
_ ->
Error(
error.TypeError(type_error.TypeError(
"avg function can only be used on floats, integers, and USD.",
)),
)
}
}
expr.LabelDef(txt) -> {
// Duplicate label definitions should be a type error
let defs =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ pub type TypedExpr {
)
Group(type_: typ.Typ, expr: TypedExpr)
BuiltinSum(type_: typ.Typ, keys: List(grid.GridKey))
BuiltinAvg(type_: typ.Typ, keys: List(grid.GridKey))
}

pub fn visit_cross_labels(
Expand Down Expand Up @@ -97,6 +98,7 @@ fn do_to_string(te: TypedExpr) -> String {
<> " "
<> do_to_string(rhs)
BuiltinSum(_, _) -> "sum"
BuiltinAvg(_, _) -> "avg"
UsdLiteral(_, dollars) -> {
let str = "$" <> rational.to_string(dollars, 100, False)
case string.split_once(str, ".") {
Expand Down
4 changes: 4 additions & 0 deletions src/squared_away/squared_away_lang/util/rational.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ pub fn sum(rats: List(Rat)) -> Rat {
list.fold(rats, from_int(0), add)
}

pub fn avg(rats: List(Rat)) -> Rat {
sum(rats) |> divide(list.length(rats) |> from_int)
}

pub fn to_string(rat: Rat, precision: Int, with_commas: Bool) -> String {
let Rat(n, d) = rat
let whole = bigi.to_string(bigi.divide(n, d))
Expand Down

0 comments on commit 020b07f

Please sign in to comment.