Skip to content

Commit

Permalink
Merge pull request #23 from mint-lang/decode
Browse files Browse the repository at this point in the history
Automatic decoders for primitive types and records
  • Loading branch information
gdotdesign authored Jun 6, 2018
2 parents 4c86f5a + ac1eef6 commit 4d36872
Show file tree
Hide file tree
Showing 31 changed files with 604 additions and 9 deletions.
36 changes: 36 additions & 0 deletions spec/compilers/decode
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
record X {
name : String
}

component A {
fun x (input : Object) : Result(Object.Error, x) {
decode input as X
}

fun render : Html {
<div/>
}
}
--------------------------------------------------------------------------------
const $$X = (input) => {
let name = Decoder.field(`name`, Decoder.string)(input)
if (name instanceof Err) {
return name
}

return new Ok({
name: name.value
})
}

class $A extends Component {
x(input) {
return $$X(input)
}

render() {
return _createElement("div", {})
}
}

$A.displayName = "A"
93 changes: 93 additions & 0 deletions spec/compilers/decoder
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
record Y {
size : Number from "SIIIZEEE"
}

record X {
string : String,
number : Number,
bool : Bool,
time : Time,
maybe : Maybe(String),
array : Array(String),
y : Y
}

component A {
fun x (input : Object) : Result(Object.Error, x) {
decode input as X
}

fun render : Html {
<div/>
}
}
--------------------------------------------------------------------------------
const $$Y = (input) => {
let size = Decoder.field(`SIIIZEEE`, Decoder.number)(input)
if (size instanceof Err) {
return size
}

return new Ok({
size: size.value
})
}

const $$X = (input) => {
let string = Decoder.field(`string`, Decoder.string)(input)
if (string instanceof Err) {
return string
}

let number = Decoder.field(`number`, Decoder.number)(input)
if (number instanceof Err) {
return number
}

let bool = Decoder.field(`bool`, Decoder.boolean)(input)
if (bool instanceof Err) {
return bool
}

let time = Decoder.field(`time`, Decoder.time)(input)
if (time instanceof Err) {
return time
}

let maybe = Decoder.field(`maybe`, Decoder.maybe(Decoder.string))(input)
if (maybe instanceof Err) {
return maybe
}

let array = Decoder.field(`array`, Decoder.array(Decoder.string))(input)
if (array instanceof Err) {
return array
}

let y = Decoder.field(`y`, $$Y)(input)
if (y instanceof Err) {
return y
}

return new Ok({
string: string.value,
number: number.value,
bool: bool.value,
time: time.value,
maybe: maybe.value,
array: array.value,
y: y.value
})
}

class $A extends Component {
x(input) {
return $$X(input)
}

render() {
return _createElement("div", {})
}
}

$A.displayName = "A"
19 changes: 19 additions & 0 deletions spec/compilers/enum
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
enum Test {
X,
Y
}

module X {
fun a : Test {
Test::X
}
}
--------------------------------------------------------------------------------
$Test_X = Symbol.for(`Test_X`)
$Test_Y = Symbol.for(`Test_Y`)

const $X = new(class {
a() {
return $Test_X
}
})
29 changes: 29 additions & 0 deletions spec/formatters/decode
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
record X {
name : String
}

component A {
fun x (input : Object) : Result(Object.Error, x) {
decode
input as
X
}

fun render : Html {
<div/>
}
}
--------------------------------------------------------------------------------
record X {
name : String
}

component A {
fun x (input : Object) : Result(Object.Error, x) {
decode input as X
}

fun render : Html {
<div/>
}
}
6 changes: 6 additions & 0 deletions spec/formatters/record_definition_with_from
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
recordTest{a:String from "blah",b:Blah from "what"}
--------------------------------------------------------------------------------
record Test {
a : String from "blah",
b : Blah from "what"
}
18 changes: 18 additions & 0 deletions spec/parsers/decode.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
require "../spec_helper"

describe "Decode Expression" do
subject decode

expect_ignore "."
expect_ignore "::"
expect_ignore "asd"

expect_error "decode", Mint::Parser::DecodeExpectedExpression
expect_error "decode x", Mint::Parser::DecodeExpectedAs
expect_error "decode x x", Mint::Parser::DecodeExpectedAs
expect_error "decode x ?", Mint::Parser::DecodeExpectedAs
expect_error "decode x as", Mint::Parser::DecodeExpectedType
expect_error "decode x as x", Mint::Parser::DecodeExpectedType

expect_ok "decode x as T"
end
1 change: 1 addition & 0 deletions spec/parsers/record_definition_field_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ describe "Record Definition Field" do
expect_error "asd", Mint::Parser::RecordDefinitionFieldExpectedColon
expect_error "asd:", Mint::Parser::RecordDefinitionFieldExpectedType
expect_error "asd: ", Mint::Parser::RecordDefinitionFieldExpectedType
expect_error "asd: T from", Mint::Parser::RecordDefinitionFieldExpectedMapping

expect_ok "asd: T"
expect_ok "asd : T"
Expand Down
55 changes: 55 additions & 0 deletions spec/type_checking/decode
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
record X {
name : String
}

component A {
fun x (input : Object) : Result(Object.Error, x) {
decode input as X
}

fun render : Html {
<div/>
}
}
------------------------------------------------------------DecodeExpectedObject
record X {
name : String
}

component A {
fun x (input : String) : Result(Object.Error, x) {
decode input as X
}

fun render : Html {
<div/>
}
}
---------------------------------------------------------------DecodeComplexType
record X {
name : Blah
}

component A {
fun x (input : Object) : Result(Object.Error, x) {
decode input as X
}

fun render : Html {
<div/>
}
}
---------------------------------------------------------------DecodeComplexType
record X {
name : Maybe(a)
}

component A {
fun x (input : Object) : Result(Object.Error, x) {
decode input as X
}

fun render : Html {
<div/>
}
}
2 changes: 1 addition & 1 deletion src/assets/runtime.js

Large diffs are not rendered by default.

14 changes: 14 additions & 0 deletions src/ast/decode.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module Mint
class Ast
class Decode < Node
getter expression, type

def initialize(@expression : Expression,
@input : Data,
@from : Int32,
@type : Type,
@to : Int32)
end
end
end
end
5 changes: 3 additions & 2 deletions src/ast/record_definition_field.cr
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
module Mint
class Ast
class RecordDefinitionField < Node
getter key, type
getter key, type, mapping

def initialize(@key : Variable,
def initialize(@mapping : StringLiteral?,
@key : Variable,
@input : Data,
@from : Int32,
@type : Type,
Expand Down
1 change: 1 addition & 0 deletions src/compiler.cr
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ module Mint
to: @artifacts

def initialize(@artifacts : TypeChecker::Artifacts)
@decoder = Decoder.new
end

# Helper for converting type ids
Expand Down
13 changes: 13 additions & 0 deletions src/compilers/decode.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module Mint
class Compiler
def compile(node : Ast::Decode) : String
expression =
compile node.expression

code =
@decoder.generate types[node]

"#{code}(#{expression})"
end
end
end
2 changes: 1 addition & 1 deletion src/compilers/enum.cr
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ module Mint
full_name =
prefix + "_" + name

"$#{full_name} = Symbol(`#{full_name}`)"
"$#{full_name} = Symbol.for(`#{full_name}`)"
end.join("\n")
end
end
Expand Down
6 changes: 5 additions & 1 deletion src/compilers/top_level.cr
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ module Mint
enums =
compile ast.enums

decoders =
@decoder.compile

media_css =
medias.map do |condition, rules|
selectors =
Expand Down Expand Up @@ -115,7 +118,7 @@ module Mint
"_insertStyles(`\n#{css + media_css}\n`)"
end

(enums + providers + routes + modules + stores + components + [footer])
(enums + [decoders] + providers + routes + modules + stores + components + [footer])
.reject(&.empty?)
.join("\n\n")
end
Expand Down Expand Up @@ -146,6 +149,7 @@ module Mint
const ReactDOM = Mint.ReactDOM;
const Provider = Mint.Provider;
const Nothing = Mint.Nothing;
const Decoder = Mint.Decoder;
const DateFNS = Mint.DateFNS;
const Record = Mint.Record;
const Store = Mint.Store;
Expand Down
13 changes: 13 additions & 0 deletions src/formatters/decode.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module Mint
class Formatter
def format(node : Ast::Decode)
expression =
format node.expression

type =
format node.type

"decode #{expression} as #{type}"
end
end
end
10 changes: 9 additions & 1 deletion src/formatters/record_definition_field.cr
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,15 @@ module Mint
type =
format node.type

"#{key} : #{type}"
mapping =
node.mapping.try do |item|
mapping_key =
format item

" from #{mapping_key}"
end.to_s

"#{key} : #{type}#{mapping}"
end
end
end
Loading

0 comments on commit 4d36872

Please sign in to comment.