view modules/json.tp @ 367:e2c1309ab750

Use dict hash instead of dict linear in JSON parser. Implement basic string escapes in JSON parser.
author Michael Pavone <pavone@retrodev.com>
date Sun, 09 Aug 2015 18:07:23 -0700
parents 3d36d69aab7f
children
line wrap: on
line source

{
	startArr <- "[" byte: 0
	endArr <- "]" byte: 0
	startObj <- "{" byte: 0
	endObj <- "}" byte: 0
	quote <- "\"" byte: 0
	esc <- "\\" byte: 0
	zero <- "0" byte: 0
	nine <- "9" byte: 0
	neg <- "-" byte: 0
	space <- " " byte: 0
	comma <- "," byte: 0
	period <- "." byte: 0
	tab <- "	" byte: 0
	nl <- "\n" byte: 0
	cr <- "\r" byte: 0
	colon <- ":" byte: 0
	t <- "t" byte: 0
	f <- "f" byte: 0

	parseNumAt <- :str at {
		num <- 0
		l <- str length
		minus <- false
		aft <- -1
		ignore <- false
		while: { at < l } do: {
			b <- str byte: at
			if: b = neg {
				minus <- true
			} else: {
				if: b = period {
					ignore <- true
				} else: {
					if: b >= zero && b <= nine {
						if: (not: ignore) {
							num <- num * 10 + (str byte: at) - zero
						}
					} else: {
						aft <- at
						at <- l
					}
				}
			}
			at <- at + 1
		}
		if: aft < 0 {
			aft <- at
		}
		if: minus {
			num <- 0 - num
		}
		#{
			value <- num
			after <- aft
		}
	}

	parseStrAt <- :src at {
		//TODO: Deal with unicode escapes
		bslash <- "\\" byte: 0
		quote <- "\"" byte: 0
		escapes <- dict hash
		escapes set: bslash "\\"
		escapes set: quote "\""
		escapes set: ("n" byte: 0) "\n"
		escapes set: ("t" byte: 0) "\t"
		escapes set: ("r" byte: 0) "\r"
		i <- at + 1
		chunks <- #[]
		start <- i
		continue <- true
		while: { continue && i < (src byte_length) } do: {
			b <- src byte: i
			if: b = bslash {
				if: i > start {
					chunks append: (src from: start withLength: i-start)
				}
				if: i + 1 < (src byte_length) {
					i <- i + 1
					b <- src byte: i
					start <- i + 1
					escapes ifget: b :v {
						chunks append: v
					} else: {
						//not a recognized escape, just copy it raw
						chunks append: (src from: i-1 withLength: 2)
					}
				}
			} else: {
				if: b = quote {
					if: i > start {
						chunks append: (src from: start withLength: i-start)
					}
					continue <- false
				}
			}
			i <- i + 1
		}
		#{
			value <- chunks join: ""
			after <- i
		}
	}

	_decode:at <- :text :cur {
		ret <- false
		b <- text byte: cur
		if: b = neg || b >= zero && b <= nine {
			parseNumAt: text cur
		} else: {
			if: b = quote {
				parseStrAt: text cur
			} else: {
				if: b = startArr {
					len <- text length
					val <- #[]
					cur <- cur + 1
					aft <- -1
					while: { cur < len } do: {
						b <- text byte: cur
						if: b = endArr {
							aft <- cur + 1
							cur <- len
						} else: {
							if: b = comma || b = space || b = tab || b = nl || b = cr {
								cur <- cur + 1
							} else: {
								el <- _decode: text at: cur
								cur <- el after
								val append: (el value)
							}
						}
					}
					#{
						value <- val
						after <- aft
					}
				} else: {
					if: b = startObj {
						len <- text length
						val <- dict hash
						cur <- cur + 1
						aft <- -1
						expectKey <- true
						key <- ""
						while: { cur < len } do: {
							b <- text byte: cur
							if: b = comma || b = space || b = tab || b = colon || b = nl || b = cr {
								cur <- cur + 1
							} else: {
								if: expectKey {
									if: b = endObj {
										aft <- cur + 1
										cur <- len
									} else: {
										kd <- _decode: text at: cur
										key <- kd value
										cur <- kd after

										expectKey <- false
									}
								} else: {
									el <- _decode: text at: cur
									val set: key (el value)
									cur <- el after
									expectKey <- true
								}
							}
						}
						#{
							after <- aft
							value <- val
						}
					} else: {
						if: b = t && (text from: cur withLength: 4) = "true" {
							#{
								value <- true
								after <- cur + 4
							}
						} else: {
							if: b = f && (text from: cur withLength: 5) = "false" {
								#{
									value <- false
									after <- cur + 5
								}
							} else: {
								#{
									value <- "foobar"
									after <- (text length)
								}
							}
						}

					}
				}
			}
		}
	}
	#{
		decode <- :text {
			(_decode: text at: 0) value
		}

		encode <- :value {
			jsonEncoder encode: value
		}

		main <- {
			o <- #{
				foo <- "bar"
				baz <- ["fizz" "buzz" "buzzzz"]
				qux <- ((dict hash) set: "fo" "shizzle") set: "my" "nizzle"
				arr <- #["pirate" "booty"]
				numbers <- [1 2 3 42 (0-1337) 3.14159]
				booleans <- [
					true
					false
				]
			}
			print: (encode: o) . "\n"
			0
		}
	}
}