Skip to content

Commit

Permalink
make sum interpret correctly
Browse files Browse the repository at this point in the history
  • Loading branch information
bcpeinhardt committed Nov 4, 2024
1 parent e447dd9 commit 152295d
Show file tree
Hide file tree
Showing 14 changed files with 250 additions and 62 deletions.
4 changes: 2 additions & 2 deletions squared_away/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
<title>🚧 form</title>

<style>
input.errorcell {
/* input.errorcell {
outline: 1px solid maroon;
border: 1px solid maroon;
}
} */
</style>

<!-- Uncomment this if you add the TailwindCSS integration -->
Expand Down
26 changes: 22 additions & 4 deletions squared_away/src/squared_away.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ import squared_away_lang/grid
import squared_away_lang/interpreter/value
import squared_away_lang/typechecker/typed_expr

const initial_grid_width = 5
const initial_grid_width = 7

const initial_grid_height = 5
const initial_grid_height = 20

pub fn main() {
let app = lustre.application(init, update, view)
Expand Down Expand Up @@ -86,7 +86,7 @@ type Msg {
}

fn update_grid(model: Model) -> Model {
let scanned = lang.scan_grid(model.src_grid)
let scanned = lang.scan_grid(model.src_grid) |> io.debug
let parsed = lang.parse_grid(scanned)
let typechecked = lang.typecheck_grid(parsed)
let value_grid = lang.interpret_grid(typechecked)
Expand Down Expand Up @@ -336,7 +336,7 @@ fn view(model: Model) -> element.Element(Msg) {
grid.get(model.src_grid, key)
DisplayValues, _ ->
case grid.get(model.value_grid, key) {
Error(e) -> error.error_type_string(e)
Error(_) -> grid.get(model.src_grid, key)
Ok(v) -> value.value_to_string(v)
}
}
Expand All @@ -349,6 +349,17 @@ fn view(model: Model) -> element.Element(Msg) {
True -> attribute.class("errorcell")
}

let #(color, background_color) = case
grid.get(model.value_grid, key)
{
Error(_) -> #("#b30000", "#ffe6e6")
Ok(v) ->
case v {
value.Text(_) -> #("#4a4a4a", "#f2f2f2")
_ -> #("black", "white")
}
}

let input =
html.input([
on_input,
Expand All @@ -360,6 +371,10 @@ fn view(model: Model) -> element.Element(Msg) {
id,
attribute.type_("text"),
error_class,
attribute.style([
#("background-color", background_color),
#("color", color),
]),
])

case model.display_coords {
Expand Down Expand Up @@ -422,7 +437,10 @@ fn view(model: Model) -> element.Element(Msg) {
formula_mode_toggle_label,
grid_mode_toggle,
grid_mode_toggle_label,
html.br([]),
html.br([]),
grid,
html.br([]),
error_to_display,
])
}
Expand Down
38 changes: 8 additions & 30 deletions squared_away_lang/src/squared_away_lang.gleam
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import gleam/float
import gleam/int
import gleam/list
import gleam/option
import gleam/result
import squared_away_lang/error
import squared_away_lang/grid
Expand All @@ -25,37 +28,20 @@ pub fn interpret_grid(
pub fn typecheck_grid(
input: grid.Grid(Result(expr.Expr, error.CompileError)),
) -> grid.Grid(Result(typed_expr.TypedExpr, error.CompileError)) {
use key, expr <- grid.map_values(input)
use _, expr <- grid.map_values(input)
case expr {
Error(e) -> Error(e)
Ok(expr.Label(txt)) -> {
case grid.cell_to_the_right(input, key) {
Error(Nil) -> Ok(typed_expr.Label(type_: typ.TNil, txt:))
Ok(new_key) -> {
let val = grid.get(input, new_key)
case val {
Error(e) -> Error(e)
Ok(val) -> {
case typechecker.typecheck(input, val) {
Error(e) -> Error(e)
Ok(typed_val) ->
Ok(typed_expr.Label(type_: typed_val.type_, txt:))
}
}
}
}
}
}
Ok(expr) -> typechecker.typecheck(input, expr)
}
}

pub fn parse_grid(
input: grid.Grid(Result(List(token.Token), error.CompileError)),
) -> grid.Grid(Result(expr.Expr, error.CompileError)) {
use _, toks <- grid.map_values(input)
use key, toks <- grid.map_values(input)
case toks {
Error(e) -> Error(e)
Ok([token.BuiltinSum]) -> Ok(expr.BuiltinSum(option.Some(key)))
Ok(toks) -> {
let expr = parser.parse(toks)
expr |> result.map_error(error.ParseError)
Expand All @@ -67,14 +53,6 @@ pub fn scan_grid(
input: grid.Grid(String),
) -> grid.Grid(Result(List(token.Token), error.CompileError)) {
use _, src <- grid.map_values(input)
let maybe_scanned =
scanner.scan(src)
|> result.map_error(error.ScanError)
|> result.map(list.map(_, fn(t) {
case t {
token.LabelDef(txt) -> token.LabelDef(txt)
_ -> t
}
}))
maybe_scanned
scanner.scan(src)
|> result.map_error(error.ScanError)
}
2 changes: 2 additions & 0 deletions squared_away_lang/src/squared_away_lang/error.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ pub fn to_renderable_error(ce: CompileError) -> renderable_error.RenderableError
TypeError(te) -> type_error.to_renderable_error(te)
ParseError(pe) ->
renderable_error.RenderableError(title: "", info: pe.context, hint: None)
RuntimeError(re) ->
renderable_error.RenderableError(title: "", info: re.context, hint: None)
_ ->
renderable_error.RenderableError(
title: "Compiler error",
Expand Down
62 changes: 59 additions & 3 deletions squared_away_lang/src/squared_away_lang/interpreter.gleam
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import gleam/float
import gleam/int
import gleam/io
import gleam/list.{Continue, Stop}
import gleam/option.{None, Some}
import gleam/result
Expand All @@ -8,6 +9,7 @@ import squared_away_lang/grid
import squared_away_lang/interpreter/runtime_error
import squared_away_lang/interpreter/value
import squared_away_lang/parser/expr
import squared_away_lang/typechecker/typ
import squared_away_lang/typechecker/typed_expr

pub fn interpret(
Expand All @@ -25,6 +27,7 @@ pub fn interpret(
}
}
typed_expr.Label(_, txt) -> {
io.debug(txt)
let key =
env
|> grid.to_list
Expand Down Expand Up @@ -53,7 +56,16 @@ pub fn interpret(
case grid.get(env, key) {
Error(e) -> Error(e)
Ok(te) -> {
interpret(env, te)
case te {
typed_expr.Label(_, ltxt) if ltxt == txt -> {
Error(
error.RuntimeError(runtime_error.RuntimeError(
"Label points to itself",
)),
)
}
_ -> interpret(env, te)
}
}
}
}
Expand Down Expand Up @@ -155,9 +167,53 @@ pub fn interpret(
value.Boolean(a), expr.NotEqualCheck, value.Boolean(b) ->
Ok(value.Boolean(a != b))

_, _, _ ->
panic as "these should be the only options if the typechecker is working properly"
lhs, op, rhs -> {
let msg =
"these should be the only options if the typechecker is working properly. "
<> value.value_to_string(lhs)
<> expr.binary_to_string(op)
<> value.value_to_string(rhs)
panic as msg
}
}
}
// This will get interpreted in the parent grid-aware func to have access to keys.
typed_expr.BuiltinSum(type_:, keys:) -> {
// At this point, we've validated that the values to sum are either
// all ints or all floats, so we'll match on the type and sum them appropriately
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
}
}
})

let val = case type_ {
typ.TFloat ->
list.map(values, fn(v) {
let assert value.FloatingPointNumber(f) = v
f
})
|> float.sum
|> value.FloatingPointNumber
typ.TInt ->
list.map(values, fn(v) {
let assert value.Integer(i) = v
i
})
|> int.sum
|> value.Integer
_ -> panic as "internal compiler error sum function interpret"
}

Ok(val)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import gleam/bool
import gleam/float
import gleam/int
import gleam/string

pub type Value {
Empty
Expand All @@ -15,7 +16,7 @@ pub fn value_to_string(fv: Value) -> String {
Empty -> ""
Text(t) -> t
Integer(n) -> int.to_string(n)
Boolean(b) -> bool.to_string(b)
Boolean(b) -> bool.to_string(b) |> string.uppercase
FloatingPointNumber(f) -> float.to_string(f)
}
}
6 changes: 3 additions & 3 deletions squared_away_lang/src/squared_away_lang/parser.gleam
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import gleam/bool
import gleam/io
import gleam/option
import gleam/result
import gleam/string
import squared_away_lang/parser/expr
Expand All @@ -13,8 +13,6 @@ pub fn parse(
case rest {
[] -> Ok(expr)
_ -> {
io.debug(expr)
io.debug(rest)
Error(parse_error.ParseError(
"After parsing there were leftover tokens " <> string.inspect(rest),
))
Expand All @@ -27,6 +25,8 @@ fn do_parse(
) -> Result(#(expr.Expr, List(token.Token)), parse_error.ParseError) {
case tokens {
[] -> Ok(#(expr.Empty, []))
// We'll enrich with the grid key on the larger parsing loop
[token.BuiltinSum, ..rest] -> Ok(#(expr.BuiltinSum(option.None), rest))
[token.LabelDef(str), ..rest] -> Ok(#(expr.LabelDef(str), rest))
[token.Label(row), token.Underscore, token.Label(col), ..rest] -> {
case try_parse_binary_ops(rest) {
Expand Down
4 changes: 4 additions & 0 deletions squared_away_lang/src/squared_away_lang/parser/expr.gleam
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import gleam/option
import squared_away_lang/grid

pub type Expr {
Empty
FloatLiteral(f: Float)
Expand All @@ -9,6 +12,7 @@ pub type Expr {
UnaryOp(op: UnaryOpKind, expr: Expr)
BinaryOp(lhs: Expr, op: BinaryOpKind, rhs: Expr)
Group(inner: Expr)
BuiltinSum(key: option.Option(grid.GridKey))
}

pub type BinaryOpKind {
Expand Down
40 changes: 37 additions & 3 deletions squared_away_lang/src/squared_away_lang/scanner.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,37 @@ import squared_away_lang/scanner/token

pub fn scan(src: String) -> Result(List(token.Token), scan_error.ScanError) {
case string.trim(src) {
// No tokens will become an empty type
"" -> Ok([])

// Boolean literal
"TRUE" -> Ok([token.TrueToken])
"FALSE" -> Ok([token.FalseToken])

// A formual starts with an = sign
"=" <> rest -> do_scan(rest |> string.trim_left, [])
_ -> Ok([token.LabelDef(src |> string.trim)])
txt -> {
// We need to try and parse the text as a number literal
case float.parse(txt) {
Ok(f) -> Ok([token.FloatLiteral(f)])
Error(_) ->
case int.parse(txt) {
Ok(i) -> Ok([token.IntegerLiteral(i)])

// If it's not a Bool. Float, Or Int Literal, and it doesn't
// start with a = sign, it's a LabelDef
Error(_) -> {
case parse_identifier(txt, "") {
Ok(#(ident, "")) -> Ok([token.LabelDef(ident)])
_ ->
Error(scan_error.ScanError(
"Not a label definition, boolean, float, or integer. Are you possibly missing an `=` sign?",
))
}
}
}
}
}
}
}

Expand Down Expand Up @@ -42,6 +70,7 @@ fn do_scan(
"(" <> rest -> do_scan(string.trim_left(rest), [token.LParen, ..acc])
")" <> rest -> do_scan(string.trim_left(rest), [token.RParen, ..acc])
"_" <> rest -> do_scan(string.trim_left(rest), [token.Underscore, ..acc])
"sum" <> rest -> do_scan(string.trim_left(rest), [token.BuiltinSum, ..acc])
_ -> {
case parse_integer(src, "") {
Ok(#(n, rest)) -> {
Expand All @@ -50,7 +79,9 @@ fn do_scan(
"." <> rest -> {
use #(m, rest) <- result.try(
parse_integer(rest, "")
|> result.replace_error(scan_error.ScanError),
|> result.replace_error(scan_error.ScanError(
"Expected more digits after the decimal.",
)),
)
let assert Ok(f) =
float.parse(int.to_string(n) <> "." <> int.to_string(m))
Expand All @@ -63,7 +94,10 @@ fn do_scan(

Error(_) -> {
case parse_identifier(src, "") {
Error(Nil) -> Error(scan_error.ScanError)
Error(Nil) ->
Error(scan_error.ScanError(
"Could not understand provided txt: " <> src,
))
Ok(#(ident, rest)) ->
do_scan(string.trim_left(rest), [token.Label(ident), ..acc])
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
pub type ScanError {
ScanError
ScanError(context: String)
}

pub fn to_string(se: ScanError) {
case se {
ScanError -> "Scan Error"
ScanError(txt) -> "Scan Error: " <> txt
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,5 @@ pub type Token {
Label(key: String)
LabelDef(txt: String)
Underscore
BuiltinSum
}
Loading

0 comments on commit 152295d

Please sign in to comment.