toml: adding support for Tom's Obvious Markup Language (TOML).
							parent
							
								
									1d3779e3ba
								
							
						
					
					
						commit
						cb854c0801
					
				| 
						 | 
				
			
			@ -0,0 +1 @@
 | 
			
		|||
John Benediktsson
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1 @@
 | 
			
		|||
Parsers for Tom's Obvious, Minimal Language (TOML).
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,17 @@
 | 
			
		|||
USING: help.markup help.syntax kernel strings ;
 | 
			
		||||
IN: toml
 | 
			
		||||
 | 
			
		||||
HELP: toml>
 | 
			
		||||
{ $values { "string" string } { "assoc" object } }
 | 
			
		||||
{ $description "Decodes a configuration from the TOML format, represented as a " { $link string } "." } ;
 | 
			
		||||
 | 
			
		||||
ARTICLE: "toml" "Tom's Obvious Markup Language (TOML)"
 | 
			
		||||
"Tom's Obvious Markup Language (TOML) is described further in "
 | 
			
		||||
{ $url "https://en.wikipedia.org/wiki/TOML" } "."
 | 
			
		||||
$nl
 | 
			
		||||
"Decoding support for the TOML protocol:"
 | 
			
		||||
{ $subsections
 | 
			
		||||
    toml>
 | 
			
		||||
} ;
 | 
			
		||||
 | 
			
		||||
ABOUT: "toml"
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,122 @@
 | 
			
		|||
USING: multiline toml tools.test ;
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
    H{
 | 
			
		||||
        { "title" "TOML Example" }
 | 
			
		||||
        { "hosts" { "alpha" "omega" } }
 | 
			
		||||
        {
 | 
			
		||||
            "owner"
 | 
			
		||||
            H{
 | 
			
		||||
                { "name" "Tom Preston-Werner" }
 | 
			
		||||
                { "organization" "GitHub" }
 | 
			
		||||
                {
 | 
			
		||||
                    "bio"
 | 
			
		||||
                    "GitHub Cofounder & CEO\nLikes tater tots and beer."
 | 
			
		||||
                }
 | 
			
		||||
                { "dob" "1979-05-27T07:32:00Z" }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        {
 | 
			
		||||
            "database"
 | 
			
		||||
            H{
 | 
			
		||||
                { "server" "192.168.1.1" }
 | 
			
		||||
                { "ports" { 8001 8001 8002 } }
 | 
			
		||||
                { "connection_max" 5000 }
 | 
			
		||||
                { "enabled" t }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        {
 | 
			
		||||
            "servers"
 | 
			
		||||
            H{
 | 
			
		||||
                {
 | 
			
		||||
                    "alpha"
 | 
			
		||||
                    H{
 | 
			
		||||
                        { "ip" "10.0.0.1" }
 | 
			
		||||
                        { "dc" "eqdc10" }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                {
 | 
			
		||||
                    "beta"
 | 
			
		||||
                    H{
 | 
			
		||||
                        { "ip" "10.0.0.2" }
 | 
			
		||||
                        { "dc" "eqdc10" }
 | 
			
		||||
                        { "country" "中国" }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        {
 | 
			
		||||
            "clients"
 | 
			
		||||
            H{
 | 
			
		||||
                { "data" { { "gamma" "delta" } { 1 2 } } }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        {
 | 
			
		||||
            "products"
 | 
			
		||||
            V{
 | 
			
		||||
                H{
 | 
			
		||||
                    { "name" "Hammer" }
 | 
			
		||||
                    { "sku" 738594937 }
 | 
			
		||||
                }
 | 
			
		||||
                H{
 | 
			
		||||
                    { "name" "Nail" }
 | 
			
		||||
                    { "sku" 284758393 }
 | 
			
		||||
                    { "color" "gray" }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
} [
 | 
			
		||||
    [=[
 | 
			
		||||
 | 
			
		||||
# This is a TOML document. Boom.
 | 
			
		||||
 | 
			
		||||
title = "TOML Example"
 | 
			
		||||
 | 
			
		||||
[owner]
 | 
			
		||||
name = "Tom Preston-Werner"
 | 
			
		||||
organization = "GitHub"
 | 
			
		||||
bio = "GitHub Cofounder & CEO\nLikes tater tots and beer."
 | 
			
		||||
dob = 1979-05-27T07:32:00Z # First class dates? Why not?
 | 
			
		||||
 | 
			
		||||
[database]
 | 
			
		||||
server = "192.168.1.1"
 | 
			
		||||
ports = [ 8001, 8001, 8002 ]
 | 
			
		||||
connection_max = 5000
 | 
			
		||||
enabled = true
 | 
			
		||||
 | 
			
		||||
[servers]
 | 
			
		||||
 | 
			
		||||
  # You can indent as you please. Tabs or spaces. TOML don't care.
 | 
			
		||||
  [servers.alpha]
 | 
			
		||||
  ip = "10.0.0.1"
 | 
			
		||||
  dc = "eqdc10"
 | 
			
		||||
 | 
			
		||||
  [servers.beta]
 | 
			
		||||
  ip = "10.0.0.2"
 | 
			
		||||
  dc = "eqdc10"
 | 
			
		||||
  country = "中国" # This should be parsed as UTF-8
 | 
			
		||||
 | 
			
		||||
[clients]
 | 
			
		||||
data = [ ["gamma", "delta"], [1, 2] ] # just an update to make sure parsers support it
 | 
			
		||||
 | 
			
		||||
# Line breaks are OK when inside arrays
 | 
			
		||||
hosts = [
 | 
			
		||||
  "alpha",
 | 
			
		||||
  "omega"
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
# Products
 | 
			
		||||
 | 
			
		||||
  [[products]]
 | 
			
		||||
  name = "Hammer"
 | 
			
		||||
  sku = 738594937
 | 
			
		||||
 | 
			
		||||
  [[products]]
 | 
			
		||||
  name = "Nail"
 | 
			
		||||
  sku = 284758393
 | 
			
		||||
  color = "gray"
 | 
			
		||||
 | 
			
		||||
    ]=] toml>
 | 
			
		||||
] unit-test
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,294 @@
 | 
			
		|||
! Copyright (C) 2019 John Benediktsson
 | 
			
		||||
! See http://factorcode.org/license.txt for BSD license
 | 
			
		||||
 | 
			
		||||
USING: accessors arrays assocs hashtables kernel locals make
 | 
			
		||||
math.parser peg peg.parsers regexp sequences splitting
 | 
			
		||||
strings.parser ;
 | 
			
		||||
 | 
			
		||||
IN: toml
 | 
			
		||||
 | 
			
		||||
ERROR: duplicate-key key ;
 | 
			
		||||
 | 
			
		||||
ERROR: unknown-value value ;
 | 
			
		||||
 | 
			
		||||
<PRIVATE
 | 
			
		||||
 | 
			
		||||
TUPLE: table name array? entries ;
 | 
			
		||||
 | 
			
		||||
TUPLE: entry key value ;
 | 
			
		||||
 | 
			
		||||
: boolean-parser ( -- parser )
 | 
			
		||||
    "true" token [ drop t ] action
 | 
			
		||||
    "false" token [ drop f ] action
 | 
			
		||||
    2choice ;
 | 
			
		||||
 | 
			
		||||
: digits ( parser -- parser )
 | 
			
		||||
    "_" token [ drop f ] action 2choice repeat1 [ sift ] action ;
 | 
			
		||||
 | 
			
		||||
: sign ( -- parser )
 | 
			
		||||
    "+" token "-" token 2choice ;
 | 
			
		||||
 | 
			
		||||
: hexdigit ( -- parser )
 | 
			
		||||
    [
 | 
			
		||||
        CHAR: 0 CHAR: 9 range ,
 | 
			
		||||
        CHAR: a CHAR: f range ,
 | 
			
		||||
        CHAR: A CHAR: F range ,
 | 
			
		||||
    ] choice* ;
 | 
			
		||||
 | 
			
		||||
: hex ( -- parser )
 | 
			
		||||
    "0x" token hide hexdigit digits 2seq [ first hex> ] action ;
 | 
			
		||||
 | 
			
		||||
: decdigit ( -- parser )
 | 
			
		||||
    CHAR: 0 CHAR: 9 range ;
 | 
			
		||||
 | 
			
		||||
: dec ( -- parser )
 | 
			
		||||
    decdigit digits [ dec> ] action ;
 | 
			
		||||
 | 
			
		||||
: octdigit ( -- parser )
 | 
			
		||||
    CHAR: 0 CHAR: 7 range ;
 | 
			
		||||
 | 
			
		||||
: oct ( -- parser )
 | 
			
		||||
    "0o" token hide octdigit digits 2seq [ first oct> ] action ;
 | 
			
		||||
 | 
			
		||||
: bindigit ( -- parser )
 | 
			
		||||
    CHAR: 0 CHAR: 1 range ;
 | 
			
		||||
 | 
			
		||||
: bin ( -- parser )
 | 
			
		||||
    "0b" token hide bindigit digits 2seq [ first bin> ] action ;
 | 
			
		||||
 | 
			
		||||
: integer-parser ( -- parser )
 | 
			
		||||
    hex oct bin dec 4choice [ ] action ;
 | 
			
		||||
 | 
			
		||||
: float ( -- parser )
 | 
			
		||||
    [
 | 
			
		||||
        sign optional ,
 | 
			
		||||
        decdigit digits optional ,
 | 
			
		||||
        "." token ,
 | 
			
		||||
        decdigit digits optional ,
 | 
			
		||||
        "e" token "E" token 2choice
 | 
			
		||||
        sign optional
 | 
			
		||||
        decdigit digits optional 3seq optional ,
 | 
			
		||||
    ] seq* ;
 | 
			
		||||
 | 
			
		||||
: +inf ( -- parser )
 | 
			
		||||
    "+" token optional "inf" token 2seq [ drop 1/0. ] action ;
 | 
			
		||||
 | 
			
		||||
: -inf ( -- parser )
 | 
			
		||||
    "-inf" token [ drop -1/0. ] action ;
 | 
			
		||||
 | 
			
		||||
: nan ( -- parser )
 | 
			
		||||
    sign optional "nan" token 2seq
 | 
			
		||||
    [ drop NAN: 8000000000000 ] action ;
 | 
			
		||||
 | 
			
		||||
: float-parser ( -- parser )
 | 
			
		||||
    float +inf -inf nan 4choice
 | 
			
		||||
    [ unclip-last append "" concat-as string>number ] action ;
 | 
			
		||||
 | 
			
		||||
: escaped ( -- parser )
 | 
			
		||||
    "\\" token hide [ "btnfr\"\\" member-eq? ] satisfy 2seq
 | 
			
		||||
    [ first escape ] action ;
 | 
			
		||||
 | 
			
		||||
: unicode ( -- parser )
 | 
			
		||||
    "\\u" token hide hexdigit 4 exactly-n 2seq
 | 
			
		||||
    "\\U" token hide hexdigit 8 exactly-n 2seq
 | 
			
		||||
    2choice [ first hex> ] action ;
 | 
			
		||||
 | 
			
		||||
: basic-string ( -- parser )
 | 
			
		||||
    escaped unicode [ "\"\n" member? not ] satisfy 3choice repeat0
 | 
			
		||||
    "\"" dup surrounded-by ;
 | 
			
		||||
 | 
			
		||||
: literal-string ( -- parser )
 | 
			
		||||
    [ "'\n" member? not ] satisfy repeat0
 | 
			
		||||
    "'" dup surrounded-by ;
 | 
			
		||||
 | 
			
		||||
: single-string ( -- parser )
 | 
			
		||||
    basic-string literal-string 2choice [ "" like ] action ;
 | 
			
		||||
 | 
			
		||||
: multi-basic-string ( -- parser )
 | 
			
		||||
    escaped unicode [ CHAR: \" = not ] satisfy 3choice repeat0
 | 
			
		||||
    "\"\"\"" dup surrounded-by ;
 | 
			
		||||
 | 
			
		||||
: multi-literal-string ( -- parser )
 | 
			
		||||
    [ CHAR: ' = not ] satisfy repeat0
 | 
			
		||||
    "'''" dup surrounded-by ;
 | 
			
		||||
 | 
			
		||||
: multi-string ( -- parser )
 | 
			
		||||
    multi-basic-string multi-literal-string 2choice [
 | 
			
		||||
        "" like "\n" ?head drop
 | 
			
		||||
        R/ \\[ \t\r\n]*\n[ \t\r\n]*/m "" re-replace
 | 
			
		||||
    ] action ;
 | 
			
		||||
 | 
			
		||||
: string-parser ( -- parser )
 | 
			
		||||
    multi-string single-string 2choice ;
 | 
			
		||||
 | 
			
		||||
: date-parser ( -- parser )
 | 
			
		||||
    [
 | 
			
		||||
        decdigit 4 exactly-n ,
 | 
			
		||||
        "-" token ,
 | 
			
		||||
        decdigit 2 exactly-n ,
 | 
			
		||||
        "-" token ,
 | 
			
		||||
        decdigit 2 exactly-n ,
 | 
			
		||||
    ] seq* [ "" concat-as ] action ;
 | 
			
		||||
 | 
			
		||||
: time-parser ( -- parser )
 | 
			
		||||
    [
 | 
			
		||||
        decdigit 2 exactly-n ,
 | 
			
		||||
        ":" token ,
 | 
			
		||||
        decdigit 2 exactly-n ,
 | 
			
		||||
        ":" token ,
 | 
			
		||||
        decdigit 2 exactly-n ,
 | 
			
		||||
        "." token decdigit repeat1 2seq optional ,
 | 
			
		||||
    ] seq* [ "" concat-as ] action ;
 | 
			
		||||
 | 
			
		||||
: timezone-parser ( -- parser )
 | 
			
		||||
    [
 | 
			
		||||
        "Z" token ,
 | 
			
		||||
        "-" token
 | 
			
		||||
        decdigit 2 exactly-n ":" token
 | 
			
		||||
        decdigit 2 exactly-n 4seq [ "" concat-as ] action ,
 | 
			
		||||
    ] choice* ;
 | 
			
		||||
 | 
			
		||||
: datetime-parser ( -- parser )
 | 
			
		||||
    [
 | 
			
		||||
        date-parser ,
 | 
			
		||||
        "T" token " " token 2choice ,
 | 
			
		||||
        time-parser ,
 | 
			
		||||
        timezone-parser optional ,
 | 
			
		||||
    ] seq* [ "" concat-as ] action ;
 | 
			
		||||
 | 
			
		||||
: space ( -- parser )
 | 
			
		||||
    [ " \t" member? ] satisfy repeat0 ;
 | 
			
		||||
 | 
			
		||||
: whitespace ( -- parser )
 | 
			
		||||
    [ " \t\r\n" member? ] satisfy repeat0 ;
 | 
			
		||||
 | 
			
		||||
DEFER: value-parser
 | 
			
		||||
 | 
			
		||||
: array-parser ( -- parser )
 | 
			
		||||
    [
 | 
			
		||||
        "[" token hide ,
 | 
			
		||||
        whitespace hide ,
 | 
			
		||||
        value-parser
 | 
			
		||||
        whitespace hide "," token whitespace hide 3seq list-of ,
 | 
			
		||||
        whitespace hide ,
 | 
			
		||||
        "]" token hide ,
 | 
			
		||||
    ] seq* [ first { } like ] action ;
 | 
			
		||||
 | 
			
		||||
DEFER: key-value-parser
 | 
			
		||||
 | 
			
		||||
: inline-table-parser ( -- parser )
 | 
			
		||||
    [
 | 
			
		||||
        "{" token hide ,
 | 
			
		||||
        whitespace hide ,
 | 
			
		||||
        key-value-parser
 | 
			
		||||
        whitespace hide "," token whitespace hide 3seq list-of ,
 | 
			
		||||
        whitespace hide ,
 | 
			
		||||
        "}" token hide ,
 | 
			
		||||
    ] seq* [ first >hashtable ] action ;
 | 
			
		||||
 | 
			
		||||
: value-parser ( -- parser )
 | 
			
		||||
    [
 | 
			
		||||
        [
 | 
			
		||||
            boolean-parser ,
 | 
			
		||||
            datetime-parser ,
 | 
			
		||||
            date-parser ,
 | 
			
		||||
            time-parser ,
 | 
			
		||||
            float-parser ,
 | 
			
		||||
            integer-parser ,
 | 
			
		||||
            string-parser ,
 | 
			
		||||
            [ array-parser ] box ,
 | 
			
		||||
            [ inline-table-parser ] box ,
 | 
			
		||||
        ] choice*
 | 
			
		||||
    ] delay ;
 | 
			
		||||
 | 
			
		||||
: name-parser ( -- parser )
 | 
			
		||||
    [
 | 
			
		||||
        CHAR: A CHAR: Z range ,
 | 
			
		||||
        CHAR: a CHAR: z range ,
 | 
			
		||||
        CHAR: 0 CHAR: 9 range ,
 | 
			
		||||
        "_" token [ first ] action ,
 | 
			
		||||
        "-" token [ first ] action ,
 | 
			
		||||
    ] choice* repeat1 [ "" like ] action single-string 2choice ;
 | 
			
		||||
 | 
			
		||||
: comment-parser ( -- parser )
 | 
			
		||||
    [
 | 
			
		||||
        space ,
 | 
			
		||||
        "#" token ,
 | 
			
		||||
        [ CHAR: \n = not ] satisfy repeat0 ,
 | 
			
		||||
    ] seq* [ drop f ] action ;
 | 
			
		||||
 | 
			
		||||
: key-parser ( -- parser )
 | 
			
		||||
    name-parser "." token list-of [ { } like ] action ;
 | 
			
		||||
 | 
			
		||||
: key-value-parser ( -- parser )
 | 
			
		||||
    [
 | 
			
		||||
        space hide ,
 | 
			
		||||
        key-parser ,
 | 
			
		||||
        space hide ,
 | 
			
		||||
        "=" token hide ,
 | 
			
		||||
        space hide ,
 | 
			
		||||
        value-parser ,
 | 
			
		||||
        comment-parser optional hide ,
 | 
			
		||||
    ] seq* [ first2 entry boa ] action ;
 | 
			
		||||
 | 
			
		||||
: line-parser ( -- parser )
 | 
			
		||||
    "\n" token "\r\n" token 2choice ;
 | 
			
		||||
 | 
			
		||||
:: table-name-parser ( begin end -- parser )
 | 
			
		||||
    [
 | 
			
		||||
        begin token hide ,
 | 
			
		||||
        space hide ,
 | 
			
		||||
        name-parser
 | 
			
		||||
        space hide "." token space hide 3seq list-of
 | 
			
		||||
        [ { } like ] action ,
 | 
			
		||||
        space hide ,
 | 
			
		||||
        end token hide ,
 | 
			
		||||
        comment-parser optional hide ,
 | 
			
		||||
    ] seq* ;
 | 
			
		||||
 | 
			
		||||
: table-parser ( -- parser )
 | 
			
		||||
    [
 | 
			
		||||
        space hide ,
 | 
			
		||||
        "[[" "]]" table-name-parser [ t suffix! ] action
 | 
			
		||||
        "[" "]" table-name-parser [ f suffix! ] action
 | 
			
		||||
        2choice ,
 | 
			
		||||
        whitespace hide ,
 | 
			
		||||
        key-value-parser line-parser list-of optional ,
 | 
			
		||||
    ] seq* [ first2 [ first2 ] dip table boa ] action ;
 | 
			
		||||
 | 
			
		||||
: toml-parser ( -- parser )
 | 
			
		||||
    [
 | 
			
		||||
        whitespace hide ,
 | 
			
		||||
        [
 | 
			
		||||
            comment-parser ,
 | 
			
		||||
            table-parser ,
 | 
			
		||||
            key-value-parser ,
 | 
			
		||||
        ] choice* whitespace list-of ,
 | 
			
		||||
        whitespace hide ,
 | 
			
		||||
    ] seq* [ first sift { } like ] action ;
 | 
			
		||||
 | 
			
		||||
: check-no-key ( key assoc -- key assoc )
 | 
			
		||||
    2dup at* nip [ over duplicate-key ] when ;
 | 
			
		||||
 | 
			
		||||
: deep-at ( keys assoc -- value )
 | 
			
		||||
    swap [
 | 
			
		||||
        over ?at [ nip ] [
 | 
			
		||||
            H{ } clone [ swap rot check-no-key set-at ] keep
 | 
			
		||||
        ] if
 | 
			
		||||
    ] each ;
 | 
			
		||||
 | 
			
		||||
GENERIC: update-toml ( assoc entry -- assoc )
 | 
			
		||||
 | 
			
		||||
M: entry update-toml
 | 
			
		||||
    [ key>> unclip-last [ over deep-at ] dip ] [ value>> ] bi
 | 
			
		||||
    swap rot check-no-key set-at ;
 | 
			
		||||
 | 
			
		||||
M: table update-toml
 | 
			
		||||
    [ name>> unclip-last [ over deep-at ] dip ]
 | 
			
		||||
    [ entries>> [ H{ } clone ] dip [ update-toml ] each swap rot ]
 | 
			
		||||
    [ array?>> [ push-at ] [ check-no-key set-at ] if ] tri ;
 | 
			
		||||
 | 
			
		||||
PRIVATE>
 | 
			
		||||
 | 
			
		||||
: toml> ( string -- assoc )
 | 
			
		||||
    [ H{ } clone ] dip toml-parser parse [ update-toml ] each ;
 | 
			
		||||
		Loading…
	
		Reference in New Issue