Skip to content

Commit a5a6365

Browse files
committed
save code
1 parent 9b1a7db commit a5a6365

4 files changed

Lines changed: 144 additions & 5 deletions

File tree

Binary/Basic.lean

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ def DecodeResult.toExcept : DecodeResult α → Except DecodeError α
4242
| .error err _ => .error err
4343
| .pending _ => .error (.userError "pending input")
4444

45+
@[expose]
4546
abbrev Get (α : Type) : Type := Decoder → (DecodeResult α)
4647

4748
@[always_inline]
@@ -131,7 +132,9 @@ def getThe (α : Type) [Decode α] : Get α := Decode.get (α := α)
131132
@[specialize]
132133
def DecodeResult.map (f : α → β) (x : DecodeResult α) : DecodeResult β := f <$> x
133134

135+
@[expose]
134136
abbrev Putter (α) := StateM ByteArray α
137+
@[expose]
135138
abbrev Put := Putter Unit
136139

137140
class Encode (α : Type) where

Binary/GetProc.lean

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
module
2+
3+
public import Lean
4+
import Binary.Basic
5+
import Binary.Get
6+
public import Binary.Hex
7+
8+
public meta section
9+
10+
namespace Binary
11+
12+
open Lean Meta Elab Parser Term
13+
14+
declare_syntax_cat get_proc (behavior := symbol)
15+
16+
syntax get_proc_ascription_types :=
17+
&"UInt8" <|>
18+
&"UInt16" <|>
19+
&"UInt32" <|>
20+
&"UInt64" <|>
21+
&"Int8" <|>
22+
&"Int16" <|>
23+
&"Int32" <|>
24+
&"Int64" <|>
25+
&"Float32" <|>
26+
&"Float"
27+
28+
syntax get_proc_ascription_bytes := &"bytes" term
29+
syntax get_proc_ascription := " : " (get_proc_ascription_bytes <|> (get_proc_ascription_types (" < " <|> " > ")?) <|> term)
30+
31+
syntax Parser.ident get_proc_ascription : get_proc
32+
syntax Parser.ident " ← " term : get_proc
33+
syntax &"hex " Hex.hexStr : get_proc
34+
syntax &"yield " term : get_proc
35+
syntax num get_proc_ascription : get_proc
36+
37+
syntax (name := getProcStx) "get!" "{" get_proc,*,? "}" : term
38+
39+
private def getAscriptionNumeralType (le? : Option Bool) : TSyntax ``get_proc_ascription_types → TermElabM (TSyntax `term) := fun stx => do
40+
match stx with
41+
| `(get_proc_ascription_types| UInt8) => ``(getThe UInt8)
42+
| `(get_proc_ascription_types| Int8) => ``(getThe Int8)
43+
| `(get_proc_ascription_types| $x) =>
44+
let ty := x.raw[0][0].getAtomVal
45+
let t := mkIdentFrom x (Name.mkStr1 ty)
46+
let l := mkIdentFrom x (Name.str `Binary.Primitive.LE s!"instDecode{ty}")
47+
let b := mkIdentFrom x (Name.str `Binary.Primitive.BE s!"instDecode{ty}")
48+
match le? with
49+
| .none => ``(getThe $t)
50+
| .some true => ``(@get _ $l)
51+
| .some false => ``(@get _ $b)
52+
53+
private def getAscription : TSyntax ``get_proc_ascription → TermElabM (TSyntax `term) := fun stx => do
54+
match stx with
55+
| `(get_proc_ascription| : $bs:get_proc_ascription_bytes) =>
56+
match bs with
57+
| `(get_proc_ascription_bytes| bytes $len) =>
58+
``(get_bytes $len)
59+
| _ => throwUnsupportedSyntax
60+
| `(get_proc_ascription| : $type:get_proc_ascription_types $[$tk?]?) =>
61+
let le? := tk?.map fun x => x.raw.getAtomVal.trim == " < "
62+
getAscriptionNumeralType le? type
63+
| `(get_proc_ascription| : $type:term) =>
64+
let type' ← elabType type
65+
let instType := Expr.app (Expr.const ``Decode []) type'
66+
let .some _ ← synthInstance? instType | throwErrorAt type "failed to synthesize instance {instType}"
67+
``(getThe $type)
68+
| _ => throwUnsupportedSyntax
69+
70+
private def getFileLoc (pos : String.Pos.Raw) : MetaM String := do
71+
let map ← getFileMap
72+
let pos := map.toPosition pos
73+
let fileName ← getFileName
74+
return s!"{fileName}:{pos.line}:{pos.column}: "
75+
76+
@[term_elab getProcStx]
77+
public def elabGetProcStx : TermElab := fun stx type? => do
78+
let `(getProcStx| get! { $body,* }) := stx | throwUnsupportedSyntax
79+
let es := body.getElems
80+
let mut ns := #[]
81+
let mut ts := #[]
82+
for e in es, i in List.range es.size do
83+
match e with
84+
| `(get_proc| $x:ident $ascr) =>
85+
let a ← getAscription ascr
86+
let s ← `(doSeqItem| let $x ← $a:term)
87+
ns := ns.push x
88+
ts := ts.push s
89+
| `(get_proc| $x:ident ← $action) =>
90+
let s ← `(doSeqItem| let $x ← $action:term)
91+
ns := ns.push x
92+
ts := ts.push s
93+
| `(get_proc| hex $hex:hexStr) =>
94+
let vs ← Hex.elabHexStr hex.raw[0][0].getAtomVal
95+
if vs.isEmpty then
96+
continue
97+
let pos? ← liftM <| hex.raw.getPos?.mapM getFileLoc
98+
let posStr := quote <| pos?.getD ""
99+
let f := fun (v : UInt8) => `(doSeqItem| let $(quote v.toNat):num ← getThe UInt8 | throw (DecodeError.userError s!"{$posStr:str}hex literal assertion failed"))
100+
let rs ← vs.mapM f
101+
ts := ts.append rs
102+
| `(get_proc| $n:num $ascr) =>
103+
let a ← getAscription ascr
104+
let pos? ← liftM <| n.raw.getPos?.mapM getFileLoc
105+
let posStr := quote <| pos?.getD ""
106+
let s ← `(doSeqItem| let $n:num ← $a:term | throw (DecodeError.userError s!"{$posStr:str}numeral literal assertion failed"))
107+
ts := ts.push s
108+
| `(get_proc| yield $r) =>
109+
if i != es.size - 1 then
110+
throwErrorAt e "yield must be the last element"
111+
let s ← `(doSeqItem| return $r)
112+
ts := ts.push s
113+
| _ => throwUnsupportedSyntax
114+
let s ← `(do
115+
$ts*
116+
)
117+
withMacroExpansion stx s do
118+
elabTerm s type?
119+
120+
end Binary
121+
122+
end

Binary/Hex.lean

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -83,18 +83,21 @@ where getC (c : Char) : UInt8 :=
8383

8484
syntax (name := hexStrStx) "hex!" hexStr : term
8585

86-
@[term_elab hexStrStx]
87-
public meta def elabHexStrStx : TermElab := fun hex _ => do
88-
let str := hex[1][0][0]
89-
let str := str.getAtomVal.trim
86+
public def elabHexStr : String → MetaM (Array UInt8) := fun str => do
9087
assert! str.front == '\"'
9188
assert! str.back == '\"'
9289
let content := str.toList.extract 1 (str.length - 1)
9390
let content := content.filter fun x => !x.isWhitespace
9491
if content.length % 2 != 0 then
9592
throwError "hex characters must be of even length, consider adding padding zeros"
9693
let paired := List.range (content.length / 2) |>.map fun i => (content[i * 2]!, content[i * 2 + 1]!)
97-
let data := convert_hex paired
94+
return convert_hex paired
95+
96+
@[term_elab hexStrStx]
97+
public def elabHexStrStx : TermElab := fun hex _ => do
98+
let str := hex[1][0][0]
99+
let str := str.getAtomVal.trim
100+
let data ← elabHexStr str
98101
let ts ← data.mapM fun x => `($(quote x.toNat):num)
99102
let arr ← `(ByteArray.mk #[ $ts,* ])
100103
withMacroExpansion hex arr do

Test.lean

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import Binary
22
import Binary.Hex
3+
import Binary.GetProc
34

45
open Binary Primitive LE
56

@@ -64,4 +65,14 @@ def g : IO Unit := do
6465

6566
#eval hex!"1122ABCD"
6667

68+
def t : Get (UInt32 × ByteArray) := do
69+
get! {
70+
x : UInt32,
71+
hex "ABCD",
72+
0x12 : UInt16,
73+
len : UInt32,
74+
data : bytes len.toNat,
75+
yield (x, data),
76+
}
77+
6778
def main : IO Unit := pure ()

0 commit comments

Comments
 (0)