The main objective of this PR is to shorten the frontend response time.
The major culprit was that the full documentation was serialized in each response.
This PR makes nitweb return only the synopsis and lets the frontend ask for the full documentation if needed.
Also uniformizes and cleans tabs for entities:
* Moved data between tabs (such as inheritance data to the inheritance tab)
* Added missing tabs for packages and groups (http://nitweb.moz-code.org/doc/core%3Ecollection%3E/defs)
Demo: http://nitweb.moz-code.org/
Pull-Request: #2536
Reviewed-by: Jean Privat <jean@pryen.org>
-c_src -diff
-parser.nit -diff
-parser_prod.nit -diff
-lexer.nit -diff
-tables_nit.c -diff
-c_src/** -diff
+/src/parser/parser.nit -diff
+/src/parser/parser_prod.nit -diff
+/src/parser/lexer.nit -diff
+/src/parser/tables_nit.c -diff
+/c_src/** -diff
tests/sav/**/*.res -whitespace
*.res -whitespace
init atmo do init(null, false, [0.0, 0.8*atmo_a, 1.0*atmo_a, atmo_a])
private var atmo_a = 0.05
- redef fun draw(actor, model)
+ redef fun draw(actor, model, camera)
do
var gl_error = glGetError
assert gl_error == gl_NO_ERROR else print gl_error
var program = app.globe_program
program.use
- # Set constant program values
- program.use
-
# Bind textures
glActiveTexture gl_TEXTURE0
glBindTexture(gl_TEXTURE_2D, app.texture_earth.gl_texture)
# Update camera view and light
var p = app.world_camera.position
program.camera.uniform(p.x, p.y, p.z)
- program.mvp.uniform app.world_camera.mvp_matrix
+ program.mvp.uniform camera.mvp_matrix
program.light_center.uniform(app.light.position.x, app.light.position.y, app.light.position.z)
# Set attributes
# The current entity.
protected fun entity: Entity is abstract
- redef fun start_dox_element(local_name: String, atts: Attributes) do
+ redef fun start_dox_element(local_name, atts) do
if ["briefdescription", "detaileddescription", "inbodydescription"].has(local_name) then
doc.doc = entity.doc
doc.listen_until(dox_uri, local_name)
end
# Parse the attributes of a `location` element.
- protected fun get_location(atts: Attributes): Location do
- var location = new Location
+ protected fun get_location(atts: Attributes): neo_doxygen::Location do
+ var location = new neo_doxygen::Location
location.path = atts.value_ns("", "bodyfile") or else atts.value_ns("", "file")
# Doxygen may indicate `[generated]`.
# Create a new parameter.
protected fun create_parameter: T is abstract
- redef fun start_dox_element(local_name: String, atts: Attributes) do
+ redef fun start_dox_element(local_name, atts) do
if "declname" == local_name then
text.listen_until(dox_uri, local_name)
else if "type" == local_name then
end
end
- redef fun end_dox_element(local_name: String) do
+ redef fun end_dox_element(local_name) do
if "declname" == local_name then
parameter.name = text.to_s
else if "type" == local_name then
# Return the number of type parameters.
fun arity: Int do return class_type.arity
- redef fun name=(name: String) do
+ redef fun name=(name) do
super
class_type.name = name
class_def.name = name
end
- redef fun location=(location: nullable Location) do
+ redef fun location=(location) do
super
class_def.location = location
end
class_def["mdoc"] = doc
end
- redef fun declare_super(id: String, full_name: String, prot: String, virt: String) do
+ redef fun declare_super(id, full_name, prot, virt) do
class_def.declare_super(id, full_name, prot, virt)
end
- redef fun declare_member(member: Member) do
+ redef fun declare_member(member) do
class_def.declare_member(member)
end
fun ns_separator: String do return "::"
# Set the location of the entity in the source code.
- fun location=(location: nullable Location) do
+ fun location=(location: nullable neo_doxygen::Location) do
self["location"] = location
end
# Get the location of the entity in the source code.
- fun location: nullable Location do
- return self["location"].as(nullable Location)
+ fun location: nullable neo_doxygen::Location do
+ return self["location"].as(nullable neo_doxygen::Location)
end
# Put the entity in the graph.
super Entity
init do
- self["location"] = new Location
+ self["location"] = new neo_doxygen::Location
end
- redef fun location=(location: nullable Location) do
+ redef fun location=(location) do
if location == null then
- super(new Location)
+ super(new neo_doxygen::Location)
else
super
end
# This module is used to model locations in source files.
module location
-import json::static
import json
# A location inside a source file.
super
end
- redef fun location=(location: nullable Location) do
+ redef fun location=(location) do
super
for m in inner_namespaces do m.location = location
end
- redef fun name=(name: String) do
+ redef fun name=(name) do
# Example: `MyClass.java`
super
var match = name.search_last(".")
for m in inner_namespaces do m.update_name
end
- redef fun declare_namespace(id: String, full_name: String) do
+ redef fun declare_namespace(id, full_name) do
var m: Module
assert not full_name.is_empty or id.is_empty else
var d_ns = new Namespace(graph)
var buffer = new Buffer
var root_ns = graph.by_id[""].as(Namespace)
-var location: Location
+var location
file.name = "Bar.java"
file.model_id = "_Bar_8java"
-location = new Location
+location = new neo_doxygen::Location
location.path = "a/b/Bar.java"
file.location = location
file.declare_class("classa_b_bar", "a::b::Bar", "package")
file_2.name = "Bar.java"
file_2.model_id = "_Bar_8java_2"
-location = new Location
+location = new neo_doxygen::Location
location.path = "Bar.java"
file_2.location = location
file_2.declare_namespace("namespacec", "c")
bar_class.model_id = "classa_b_bar"
bar_class.name = "Bar"
-location = new Location
+location = new neo_doxygen::Location
location.path = "a/b/Bar.class"
location.line_start = 5
location.column_start = 1
baz_class.model_id = "classbaz"
baz_class.name = "Baz"
-location = new Location
+location = new neo_doxygen::Location
location.path = "Baz.jar"
baz_class.location = location
baz_class.put_in_graph
var c = src[pos]
if not c == '<' then return new XMLError(st_loc, "Expected start of tag, got `{c}`")
pos += 1
+ if pos >= src.length then return new XMLError(st_loc, "Malformed tag")
c = src[pos]
if c == '!' then
# Special tag
# Parses an xml tag name
private fun parse_tag_name(delims: Array[Char]): String do
var idst = pos
- var c = src[pos]
var srclen = src.length
- while pos < srclen and not c.is_whitespace and not delims.has(c) do
+ while pos < srclen do
+ var c = src[pos]
+ if c.is_whitespace or delims.has(c) then break
pos += 1
- c = src[pos]
end
return src.substring(idst, pos - idst).trim
end
import model_dimensions
import particles
import selection
+import shadow
redef class App
world_camera.reset_height(10.0)
world_camera.near = 0.1
+ # Cull the invisible triangles in the back of the geometries
+ glCullFace gl_BACK
+
# Prepare programs
var programs = [versatile_program, normals_program, explosion_program, smoke_program, static_program, selection_program: GamnitProgram]
for program in programs do
# Draw all elements of `actors` and then call `frame_core_flat`
protected fun frame_core_depth(display: GamnitDisplay)
do
- glViewport(0, 0, display.width, display.height)
- frame_core_dynamic_resolution_before display
+ frame_core_depth_clock.lapse
- # Update cameras on both our programs
- versatile_program.use
- versatile_program.mvp.uniform world_camera.mvp_matrix
+ # Compute shadows
+ if light isa LightCastingShadows then
+ frame_core_shadow_prep display
+ perfs["gamnit depth shadows"].add frame_core_depth_clock.lapse
+ end
- normals_program.use
- normals_program.mvp.uniform app.world_camera.mvp_matrix
+ glViewport(0, 0, display.width, display.height)
+ frame_core_dynamic_resolution_before display
+ perfs["gamnit depth dynres"].add frame_core_depth_clock.lapse
- frame_core_depth_clock.lapse
for actor in actors do
for leaf in actor.model.leaves do
- leaf.material.draw(actor, leaf)
+ leaf.material.draw(actor, leaf, app.world_camera)
end
end
perfs["gamnit depth actors"].add frame_core_depth_clock.lapse
perfs["gamnit depth ui_sprites"].add frame_core_depth_clock.lapse
frame_core_dynamic_resolution_after display
+
+ # Debug, show the light point of view
+ #frame_core_shadow_debug display
end
private var frame_core_depth_clock = new Clock
# This method is called on many materials for many `actor` and `model` at each frame.
# It is expected to use a `GLProgram` and call an equivalent to `glDrawArrays`.
# However, it should not call `glClear` nor `GamnitDisplay::flip`.
- fun draw(actor: Actor, model: LeafModel) do end
+ fun draw(actor: Actor, model: LeafModel, camera: Camera) do end
end
# Mesh with all geometry data
end
# Source of light
-#
-# Instances of this class define a light source position and type.
-class Light
-
- # TODO point light, spotlight, directional light, etc.
+abstract class Light
# Center of this light source in world coordinates
var position = new Point3d[Float](0.0, 1000.0, 0.0)
end
+# Basic light projected from a single point
+class PointLight
+ super Light
+end
+
+# Source of light casting shadows
+abstract class LightCastingShadows
+ super Light
+
+ # View from the camera, used for shadow mapping, implemented by sub-classes
+ fun camera: Camera is abstract
+end
+
redef class App
# Live actors to be drawn on screen
var actors = new Array[Actor]
# Single light of the scene
- var light = new Light
+ var light: Light = new PointLight is writable
# TODO move `actors & light` to a scene object
# TODO support more than 1 light
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# More implementations of `Light`
+module more_lights
+
+import depth_core
+
+# TODO
+#class PointLight
+#class Spotlight
+
+# Sun-like light projecting parallel rays
+class ParallelLight
+ super LightCastingShadows
+
+ # Angle to the light source, around the X axis
+ var pitch = 0.0 is writable
+
+ # Angle to the light source, around the Y axis
+ var yaw = 0.0 is writable
+
+ # Depth texture width, in world coordinates
+ var width = 100.0 is writable
+
+ # Depth texture height, in world coordinates
+ var height = 100.0 is writable
+
+ # Viewport depth, centered on `app.world_camera`
+ var depth = 500.0 is writable
+
+ redef var camera = new ParallelLightCamera(app.display.as(not null), self) is lazy
+end
+
+private class ParallelLightCamera
+ super Camera
+
+ var light: ParallelLight
+
+ # Rotation matrix produced by the current rotation of the camera
+ fun rotation_matrix: Matrix
+ do
+ var view = new Matrix.identity(4)
+ view.rotate(light.yaw, 0.0, 1.0, 0.0)
+ view.rotate(light.pitch, 1.0, 0.0, 0.0)
+ return view
+ end
+
+ redef fun mvp_matrix
+ do
+ # TODO cache
+
+ var near = -light.depth/2.0
+ var far = light.depth/2.0
+
+ var view = new Matrix.identity(4)
+ view.translate(-position.x, -position.y, -position.z)
+ view = view * rotation_matrix
+ var projection = new Matrix.orthogonal(-light.width/2.0, light.width/2.0,
+ -light.height/2.0, light.height/2.0,
+ near, far)
+ return view * projection
+ end
+end
intrude import depth_core
intrude import flat
+intrude import shadow
+import more_lights
redef class Material
# Get the default blueish material
# The RGB values should be premultiplied by the alpha value.
var specular_color: Array[Float] is writable
- redef fun draw(actor, model)
+ redef fun draw(actor, model, camera)
do
var program = app.versatile_program
program.use
+ program.mvp.uniform camera.mvp_matrix
var mesh = model.mesh
program.use_map_specular.uniform false
program.tex_coord.array_enabled = false
- # Lights
- program.light_center.uniform(app.light.position.x, app.light.position.y, app.light.position.z)
-
# Camera
- program.camera.uniform(app.world_camera.position.x, app.world_camera.position.y, app.world_camera.position.z)
+ program.camera.uniform(camera.position.x, camera.position.y, camera.position.z)
# Colors from the material
var a = actor.alpha
program.specular_color.uniform(specular_color[0]*a, specular_color[1]*a,
specular_color[2]*a, specular_color[3]*a)
+ setup_lights(actor, model, camera, program)
+
# Execute draw
if mesh.indices.is_empty then
glDrawArrays(mesh.draw_mode, 0, mesh.vertices.length/3)
glDrawElements(mesh.draw_mode, mesh.indices.length, gl_UNSIGNED_SHORT, mesh.indices_c.native_array)
end
end
+
+ private fun setup_lights(actor: Actor, model: LeafModel, camera: Camera, program: BlinnPhongProgram)
+ do
+ # TODO use a list of lights
+
+ # Light, for Lambert and Blinn-Phong
+ var light = app.light
+ if light isa ParallelLight then
+ program.light_kind.uniform 1
+
+ # Vector parallel to the light source
+ program.light_center.uniform(
+ -light.pitch.sin * light.yaw.sin,
+ light.pitch.cos,
+ -light.yaw.cos)
+ else if light isa PointLight then
+ program.light_kind.uniform 2
+
+ # Position of the light source
+ program.light_center.uniform(app.light.position.x, app.light.position.y, app.light.position.z)
+ else
+ program.light_kind.uniform 0
+ end
+
+ # Draw projected shadows?
+ if not light isa LightCastingShadows or not app.shadow_depth_texture_available then
+ program.use_shadows.uniform false
+ return
+ else program.use_shadows.uniform true
+
+ # Light point of view
+ program.light_mvp.uniform light.camera.mvp_matrix
+
+ # Depth texture
+ glActiveTexture gl_TEXTURE4
+ glBindTexture(gl_TEXTURE_2D, app.shadow_context.depth_texture)
+ program.depth_texture.uniform 4
+ program.depth_texture_size.uniform app.shadow_resolution.to_f
+ program.depth_texture_taps.uniform 2 # TODO make configurable
+ end
end
# Material with potential `diffuse_texture` and `specular_texture`
# Bump map TODO
private var normals_texture: nullable Texture = null is writable
- redef fun draw(actor, model)
+ redef fun draw(actor, model, camera)
do
var mesh = model.mesh
program.use_map_bump.uniform false
end
+ program.mvp.uniform camera.mvp_matrix
program.translation.uniform(actor.center.x, actor.center.y, actor.center.z, 0.0)
program.scale.uniform actor.scale
var xd = sample_used_texture.offset_right - xa
var ya = sample_used_texture.offset_top
var yd = sample_used_texture.offset_bottom - ya
- xd *= 0.999
- yd *= 0.999
var tex_coords = new Array[Float].with_capacity(mesh.texture_coords.length)
for i in [0..mesh.texture_coords.length/2[ do
program.normal.array_enabled = true
program.normal.array(mesh.normals, 3)
- program.light_center.uniform(app.light.position.x, app.light.position.y, app.light.position.z)
- program.camera.uniform(app.world_camera.position.x, app.world_camera.position.y, app.world_camera.position.z)
+ # Light
+ setup_lights(actor, model, camera, program)
+
+ # Camera
+ program.camera.uniform(camera.position.x, camera.position.y, camera.position.z)
if mesh.indices.is_empty then
glDrawArrays(mesh.draw_mode, 0, mesh.vertices.length/3)
class NormalsMaterial
super Material
- redef fun draw(actor, model)
+ redef fun draw(actor, model, camera)
do
var program = app.normals_program
program.use
- program.mvp.uniform app.world_camera.mvp_matrix
+ program.mvp.uniform camera.mvp_matrix
var mesh = model.mesh
// Vertex normal
attribute vec3 normal;
- // Model view projection matrix
+ // Camera model view projection matrix
uniform mat4 mvp;
uniform mat4 rotation;
// Lights config
+ uniform int light_kind;
uniform vec3 light_center;
+ uniform mat4 light_mvp;
// Coordinates of the camera
uniform vec3 camera;
varying vec3 v_normal;
varying vec4 v_to_light;
varying vec4 v_to_camera;
+ varying vec4 v_depth_pos;
void main()
{
vec4 pos = (vec4(coord.xyz * scale, 1.0) * rotation + translation);
gl_Position = pos * mvp;
+ v_depth_pos = (pos * light_mvp) * 0.5 + 0.5;
// Pass varyings to the fragment shader
v_tex_coord = vec2(tex_coord.x, 1.0 - tex_coord.y);
v_normal = normalize(vec4(normal, 0.0) * rotation).xyz;
- v_to_light = normalize(vec4(light_center, 1.0) - pos);
v_to_camera = normalize(vec4(camera, 1.0) - pos);
+
+ if (light_kind == 0) {
+ // No light
+ } else if (light_kind == 1) {
+ // Parallel
+ v_to_light = normalize(vec4(light_center, 1.0));
+ } else {
+ // Point light (and others?)
+ v_to_light = normalize(vec4(light_center, 1.0) - pos);
+ }
}
""" @ glsl_vertex_shader
varying vec3 v_normal;
varying vec4 v_to_light;
varying vec4 v_to_camera;
+ varying vec4 v_depth_pos;
// Colors
uniform vec4 ambient_color;
uniform bool use_map_normal;
uniform sampler2D map_normal;
+ // Shadow
+ uniform int light_kind;
+ uniform bool use_shadows;
+ uniform sampler2D depth_texture;
+ uniform float depth_texture_size;
+ uniform int depth_texture_taps;
+
+ // Shadow effect on the diffuse colors of the fragment at offset `x, y`
+ float shadow_lookup(vec2 depth_coord, float x, float y) {
+ float tap_width = 1.0;
+ float pixel_size = tap_width/depth_texture_size;
+
+ vec2 offset = vec2(x * pixel_size * v_depth_pos.w,
+ y * pixel_size * v_depth_pos.w);
+ depth_coord += offset;
+
+ float depth = v_depth_pos.z/v_depth_pos.w;
+ //vec2 depth_coord = v_depth_pos.xy/v_depth_pos.w;
+ if (depth_coord.x < 0.0 || depth_coord.x > 1.0 || depth_coord.y < 0.0 || depth_coord.y > 1.0) {
+ // Out of the shadow map texture
+ //gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); // debug, red out of the light view
+ return 1.0;
+ }
+
+ float shadow_depth = texture2D(depth_texture, depth_coord).r;
+ float bias = 0.0001;
+ if (shadow_depth == 1.0) {
+ // Too far to be in depth texture
+ return 1.0;
+ } else if (shadow_depth <= depth - bias) {
+ // In a shadow
+ //gl_FragColor = vec4(0.0, 0.0, 1.0, 1.0); // debug, blue shadows
+ return 0.2; // TODO replace with a configurable ambient light
+ }
+
+ //gl_FragColor = vec4(0.0, 1.0-(shadow_depth-depth), 0.0, 1.0); // debug, green lit surfaces
+ return 1.0;
+ }
+
+ // Shadow effect on the diffuse colors of the fragment
+ float shadow() {
+ if (!use_shadows) return 1.0;
+
+ vec2 depth_coord = v_depth_pos.xy/v_depth_pos.w;
+
+ float taps = float(depth_texture_taps);
+ float tap_step = 2.00/taps;
+ float sum = 0.0;
+ for (float x = -1.0; x <= 0.99; x += tap_step)
+ for (float y = -1.0; y <= 0.99; y += tap_step)
+ sum += shadow_lookup(depth_coord, x, y);
+ return sum / taps / taps;
+ }
+
void main()
{
// Normal
vec4 ambient = ambient_color;
if (use_map_ambient) ambient *= texture2D(map_ambient, v_tex_coord);
- // Diffuse Lambert light
- vec3 to_light = v_to_light.xyz;
- float lambert = clamp(dot(normal, to_light), 0.0, 1.0);
+ if (light_kind == 0) {
+ // No light, show diffuse and ambient
- vec4 diffuse = lambert * diffuse_color;
- if (use_map_diffuse) diffuse *= texture2D(map_diffuse, v_tex_coord);
+ vec4 diffuse = diffuse_color;
+ if (use_map_diffuse) diffuse *= texture2D(map_diffuse, v_tex_coord);
- // Specular Phong light
- float s = 0.0;
- if (lambert > 0.0) {
- vec3 l = reflect(-to_light, normal);
- s = clamp(dot(l, v_to_camera.xyz), 0.0, 1.0);
- s = pow(s, 8.0); // TODO make this `shininess` a material attribute
- }
+ gl_FragColor = ambient + diffuse;
+ } else {
+ // Parallel light or point light (1 or 2)
+
+ // Diffuse Lambert light
+ vec3 to_light = v_to_light.xyz;
+ float lambert = clamp(dot(normal, to_light), 0.0, 1.0);
+
+ vec4 diffuse = lambert * diffuse_color;
+ if (use_map_diffuse) diffuse *= texture2D(map_diffuse, v_tex_coord);
+
+ // Specular Phong light
+ float s = 0.0;
+ if (lambert > 0.0) {
+ // In light
+ vec3 l = reflect(-to_light, normal);
+ s = clamp(dot(l, v_to_camera.xyz), 0.0, 1.0);
+ s = pow(s, 8.0); // TODO make this `shininess` a material attribute
+
+ // Shadows
+ diffuse *= shadow();
+ }
- vec4 specular = s * specular_color;
- if (use_map_specular) specular *= texture2D(map_specular, v_tex_coord).x;
+ vec4 specular = s * specular_color;
+ if (use_map_specular) specular *= texture2D(map_specular, v_tex_coord).x;
+
+ gl_FragColor = ambient + diffuse + specular;
+ }
- gl_FragColor = ambient + diffuse + specular;
if (gl_FragColor.a < 0.01) discard;
- //gl_FragColor = vec4(normalize(normal).rgb, 1.0); // Debug
+ //gl_FragColor = vec4(normalize(normal).rgb, 1.0); // Debug normals
}
""" @ glsl_fragment_shader
# Should this program use the texture `map_diffuse`?
var use_map_diffuse = uniforms["use_map_diffuse"].as(UniformBool) is lazy
- # Diffuser texture unit
+ # Diffuse texture unit
var map_diffuse = uniforms["map_diffuse"].as(UniformSampler2D) is lazy
# Should this program use the texture `map_specular`?
# Specular color
var specular_color = uniforms["specular_color"].as(UniformVec4) is lazy
- # Center position of the light
+ # Kind of lights: 0 -> no light, 1 -> parallel, 2 -> point
+ var light_kind = uniforms["light_kind"].as(UniformInt) is lazy
+
+ # Center position of the light *or* vector to parallel light source
var light_center = uniforms["light_center"].as(UniformVec3) is lazy
+ # Light model view projection matrix
+ var light_mvp = uniforms["light_mvp"].as(UniformMat4) is lazy
+
+ # Should shadow be drawn? Would use `depth_texture` and `light_mvp`.
+ var use_shadows = uniforms["use_shadows"].as(UniformBool) is lazy
+
+ # Diffuse texture unit
+ var depth_texture = uniforms["depth_texture"].as(UniformSampler2D) is lazy
+
+ # Size, in pixels, of `depth_texture`
+ var depth_texture_size = uniforms["depth_texture_size"].as(UniformFloat) is lazy
+
+ # Times to tap the `depth_texture`, square root (set to 3 for a total of 9 taps)
+ var depth_texture_taps = uniforms["depth_texture_taps"].as(UniformInt) is lazy
+
# Camera position
var camera = uniforms["camera"].as(UniformVec3) is lazy
# Scaling per vertex
var scale = uniforms["scale"].as(UniformFloat) is lazy
- # Model view projection matrix
+ # Camera model view projection matrix
var mvp = uniforms["mvp"].as(UniformMat4) is lazy
end
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Shadow mapping using a depth texture
+#
+# The default light does not cast any shadows. It can be changed to a
+# `ParallelLight` in client games to cast sun-like shadows:
+#
+# ~~~
+# import more_lights
+#
+# var sun = new ParallelLight
+# sun.pitch = 0.25*pi
+# sun.yaw = 0.25*pi
+# app.light = sun
+# ~~~
+module shadow
+
+intrude import gamnit::depth_core
+
+redef class App
+
+ # Resolution of the shadow texture, defaults to 4096 pixels
+ #
+ # TODO make configurable / ask the hardware for gl_MAX_TEXTURE_SIZE
+ var shadow_resolution = 4096
+
+ # Are shadows supported by the current hardware configuration?
+ #
+ # The implementation may change in the future, but it currently relies on
+ # the GL extension `GL_EOS_depth_texture`.
+ var supports_shadows: Bool is lazy do
+ return display.as(not null).gl_extensions.has("GL_OES_depth_texture")
+ end
+
+ # Is `shadow_context.depth_texture` ready to be used?
+ fun shadow_depth_texture_available: Bool
+ do return supports_shadows and shadow_context.depth_texture != -1
+
+ private var shadow_depth_program = new ShadowDepthProgram
+
+ private var perf_clock_shadow = new Clock is lazy
+
+ redef fun on_create
+ do
+ super
+
+ var program = shadow_depth_program
+ program.compile_and_link
+ var error = program.error
+ assert error == null else print_error error
+ end
+
+ private var shadow_context: ShadowContext = create_shadow_context is lazy
+
+ private fun create_shadow_context: ShadowContext
+ do
+ var display = display
+ assert display != null
+
+ var context = new ShadowContext
+ context.prepare_once(display, shadow_resolution)
+ return context
+ end
+
+ # Update the depth texture from the light point of view
+ #
+ # This method updates `shadow_context.depth_texture`.
+ protected fun frame_core_shadow_prep(display: GamnitDisplay)
+ do
+ if not supports_shadows then return
+
+ var light = app.light
+ if not light isa LightCastingShadows then return
+
+ perf_clock_shadow.lapse
+
+ # Make sure there's no errors pending
+ assert glGetError == gl_NO_ERROR
+
+ # Bind the framebuffer and make sure it is OK
+ glBindFramebuffer(gl_FRAMEBUFFER, shadow_context.light_view_framebuffer)
+ assert glGetError == gl_NO_ERROR
+ assert glCheckFramebufferStatus(gl_FRAMEBUFFER) == gl_FRAMEBUFFER_COMPLETE
+
+ # Draw to fill the depth texture and only the depth
+ glViewport(0, 0, shadow_resolution, shadow_resolution)
+ glColorMask(false, false, false, false)
+ glClear gl_COLOR_BUFFER_BIT | gl_DEPTH_BUFFER_BIT
+ assert glGetError == gl_NO_ERROR
+
+ # Update light position
+ var camera = light.camera
+ camera.position.x = app.world_camera.position.x
+ camera.position.y = app.world_camera.position.y
+ camera.position.z = app.world_camera.position.z
+
+ # Draw all actors
+ for actor in actors do
+ for leaf in actor.model.leaves do
+ leaf.material.draw_depth(actor, leaf, camera)
+ end
+ end
+
+ # Take down, bring back default values
+ glBindFramebuffer(gl_FRAMEBUFFER, shadow_context.screen_framebuffer)
+ glColorMask(true, true, true, true)
+
+ perfs["gamnit shadows prep"].add perf_clock_shadow.lapse
+ end
+
+ # ---
+ # Debug: show light view in the bottom left of the screen
+
+ # Lazy load the debugging program
+ private var shadow_debug_program: LightPointOfViewProgram is lazy do
+ var program = new LightPointOfViewProgram
+ program.compile_and_link
+ var error = program.error
+ assert error == null else print_error error
+ return program
+ end
+
+ # Draw the light view in the bottom left of the screen, for debugging only
+ #
+ # The shadow depth texture is a square that can be deformed by this projection.
+ protected fun frame_core_shadow_debug(display: GamnitDisplay)
+ do
+ if not supports_shadows then
+ print_error "Error: Shadows are not supported by the current hardware configuration"
+ return
+ end
+
+ perf_clock_shadow.lapse
+
+ var program = shadow_debug_program
+
+ glBindBuffer(gl_ARRAY_BUFFER, shadow_context.buffer_array)
+ glViewport(0, 0, display.width/3, display.height/3)
+ glClear gl_DEPTH_BUFFER_BIT
+ program.use
+
+ # Uniforms
+ glActiveTexture gl_TEXTURE0
+ glBindTexture(gl_TEXTURE_2D, shadow_context.depth_texture)
+ program.texture.uniform 0
+
+ # Attributes
+ var sizeof_gl_float = 4
+ var n_floats = 3
+ glEnableVertexAttribArray program.coord.location
+ glVertexAttribPointeri(program.coord.location, n_floats, gl_FLOAT, false, 0, 0)
+ var offset = 4 * n_floats * sizeof_gl_float
+
+ n_floats = 2
+ glEnableVertexAttribArray program.tex_coord.location
+ glVertexAttribPointeri(program.tex_coord.location, n_floats, gl_FLOAT, false, 0, offset)
+ var gl_error = glGetError
+ assert gl_error == gl_NO_ERROR else print_error gl_error
+
+ # Draw
+ glDrawArrays(gl_TRIANGLE_STRIP, 0, 4)
+ gl_error = glGetError
+ assert gl_error == gl_NO_ERROR else print_error gl_error
+
+ # Take down
+ glBindBuffer(gl_ARRAY_BUFFER, 0)
+ gl_error = glGetError
+ assert gl_error == gl_NO_ERROR else print_error gl_error
+
+ sys.perfs["gamnit shadow debug"].add app.perf_clock_shadow.lapse
+ end
+end
+
+# Handles to reused GL buffers and texture
+private class ShadowContext
+
+ # Real screen framebuffer
+ var screen_framebuffer: Int = -1
+
+ # Framebuffer for the light point of view
+ var light_view_framebuffer: Int = -1
+
+ # Depth attached to `light_view_framebuffer`
+ var depth_texture: Int = -1
+
+ # Buffer name for vertex data
+ var buffer_array: Int = -1
+
+ # Prepare all attributes once per resolution change
+ fun prepare_once(display: GamnitDisplay, shadow_resolution: Int)
+ do
+ assert display.gl_extensions.has("GL_OES_depth_texture")
+
+ # Set aside the real screen framebuffer name
+ var screen_framebuffer = glGetIntegerv(gl_FRAMEBUFFER_BINDING, 0)
+ self.screen_framebuffer = screen_framebuffer
+
+ # Framebuffer
+ var framebuffer = glGenFramebuffers(1).first
+ glBindFramebuffer(gl_FRAMEBUFFER, framebuffer)
+ assert glIsFramebuffer(framebuffer)
+ self.light_view_framebuffer = framebuffer
+ var gl_error = glGetError
+ assert gl_error == gl_NO_ERROR else print_error gl_error
+
+ # Depth & texture/color
+ var textures = glGenTextures(1)
+ self.depth_texture = textures[0]
+ gl_error = glGetError
+ assert gl_error == gl_NO_ERROR else print_error gl_error
+
+ resize(display, shadow_resolution)
+ assert glCheckFramebufferStatus(gl_FRAMEBUFFER) == gl_FRAMEBUFFER_COMPLETE
+
+ # Array buffer
+ buffer_array = glGenBuffers(1).first
+ glBindBuffer(gl_ARRAY_BUFFER, buffer_array)
+ assert glIsBuffer(buffer_array)
+ gl_error = glGetError
+ assert gl_error == gl_NO_ERROR else print_error gl_error
+
+ ## coord
+ var data = new Array[Float]
+ data.add_all([-1.0, -1.0, 0.0,
+ 1.0, -1.0, 0.0,
+ -1.0, 1.0, 0.0,
+ 1.0, 1.0, 0.0])
+ ## tex_coord
+ data.add_all([0.0, 0.0,
+ 1.0, 0.0,
+ 0.0, 1.0,
+ 1.0, 1.0])
+ var c_data = new GLfloatArray.from(data)
+ glBufferData(gl_ARRAY_BUFFER, data.length*4, c_data.native_array, gl_STATIC_DRAW)
+
+ glBindBuffer(gl_ARRAY_BUFFER, 0)
+
+ gl_error = glGetError
+ assert gl_error == gl_NO_ERROR else print_error gl_error
+ end
+
+ # Init size or resize `depth_texture`
+ fun resize(display: GamnitDisplay, shadow_resolution: Int)
+ do
+ glBindFramebuffer(gl_FRAMEBUFFER, light_view_framebuffer)
+ var gl_error = glGetError
+ assert gl_error == gl_NO_ERROR else print_error gl_error
+
+ # Depth texture
+ var depth_texture = self.depth_texture
+ glActiveTexture gl_TEXTURE0
+ glBindTexture(gl_TEXTURE_2D, depth_texture)
+ glTexParameteri(gl_TEXTURE_2D, gl_TEXTURE_MIN_FILTER, gl_LINEAR)
+ glTexParameteri(gl_TEXTURE_2D, gl_TEXTURE_MAG_FILTER, gl_NEAREST)
+ glTexParameteri(gl_TEXTURE_2D, gl_TEXTURE_WRAP_S, gl_CLAMP_TO_EDGE)
+ glTexParameteri(gl_TEXTURE_2D, gl_TEXTURE_WRAP_T, gl_CLAMP_TO_EDGE)
+ gl_error = glGetError
+ assert gl_error == gl_NO_ERROR else print_error gl_error
+
+ # TODO support hardware shadows with GL ES 3.0 or GL_EXT_shadow_samplers
+ #glTexParameteri(gl_TEXTURE_2D, gl_TEXTURE_COMPARE_MODE, ...)
+
+ glTexImage2D(gl_TEXTURE_2D, 0, gl_DEPTH_COMPONENT,
+ shadow_resolution, shadow_resolution,
+ 0, gl_DEPTH_COMPONENT, gl_UNSIGNED_SHORT, new Pointer.nul)
+ gl_error = glGetError
+ assert gl_error == gl_NO_ERROR else print_error gl_error
+
+ glFramebufferTexture2D(gl_FRAMEBUFFER, gl_DEPTH_ATTACHMENT, gl_TEXTURE_2D, depth_texture, 0)
+ gl_error = glGetError
+ assert gl_error == gl_NO_ERROR else print_error gl_error
+
+ # Check if the framebuffer is complete and valid
+ assert glCheckFramebufferStatus(gl_FRAMEBUFFER) == gl_FRAMEBUFFER_COMPLETE
+
+ # Take down
+ glBindTexture(gl_TEXTURE_2D, 0)
+ glBindFramebuffer(gl_FRAMEBUFFER, 0)
+ gl_error = glGetError
+ assert gl_error == gl_NO_ERROR else print_error gl_error
+ end
+
+ var destroyed = false
+
+ fun destroy
+ do
+ if destroyed then return
+ destroyed = true
+
+ # Free the buffer
+ glDeleteBuffers([buffer_array])
+ var gl_error = glGetError
+ assert gl_error == gl_NO_ERROR else print_error gl_error
+ buffer_array = -1
+
+ # Free the array and framebuffer plus its attachments
+ glDeleteBuffers([buffer_array])
+ glDeleteFramebuffers([light_view_framebuffer])
+ glDeleteTextures([depth_texture])
+ end
+end
+
+redef class Material
+ # Optimized draw of `model`, a part of `actor`, from the view of `camera`
+ #
+ # This drawing should only produce usable depth data. The default behavior,
+ # uses `shadow_depth_program`.
+ protected fun draw_depth(actor: Actor, model: LeafModel, camera: Camera)
+ do
+ var program = app.shadow_depth_program
+ program.use
+ program.mvp.uniform camera.mvp_matrix
+
+ var mesh = model.mesh
+
+ program.translation.uniform(actor.center.x, actor.center.y, actor.center.z, 0.0)
+ program.scale.uniform actor.scale
+ program.use_map_diffuse.uniform false
+
+ program.tex_coord.array_enabled = true
+ program.tex_coord.array(mesh.texture_coords, 2)
+
+ program.coord.array_enabled = true
+ program.coord.array(mesh.vertices, 3)
+
+ program.rotation.uniform new Matrix.gamnit_euler_rotation(actor.pitch, actor.yaw, actor.roll)
+
+ if mesh.indices.is_empty then
+ glDrawArrays(mesh.draw_mode, 0, mesh.vertices.length/3)
+ else
+ glDrawElements(mesh.draw_mode, mesh.indices.length, gl_UNSIGNED_SHORT, mesh.indices_c.native_array)
+ end
+ end
+
+end
+
+# Efficiently draw actors from the light view
+class ShadowDepthProgram
+ super GamnitProgramFromSource
+
+ redef var vertex_shader_source = """
+ // Vertex coordinates
+ attribute vec4 coord;
+
+ // Vertex translation
+ uniform vec4 translation;
+
+ // Vertex scaling
+ uniform float scale;
+
+ // Vertex coordinates on textures
+ attribute vec2 tex_coord;
+
+ // Vertex normal
+ attribute vec3 normal;
+
+ // Model view projection matrix
+ uniform mat4 mvp;
+
+ // Rotation matrix
+ uniform mat4 rotation;
+
+ // Output for the fragment shader
+ varying vec2 v_tex_coord;
+
+ void main()
+ {
+ vec4 pos = (vec4(coord.xyz * scale, 1.0) * rotation + translation);
+ gl_Position = pos * mvp;
+
+ // Pass varyings to the fragment shader
+ v_tex_coord = vec2(tex_coord.x, 1.0 - tex_coord.y);
+ }
+ """ @ glsl_vertex_shader
+
+ redef var fragment_shader_source = """
+ precision mediump float;
+
+ // Diffuse map
+ uniform bool use_map_diffuse;
+ uniform sampler2D map_diffuse;
+
+ varying vec2 v_tex_coord;
+
+ void main()
+ {
+ if (use_map_diffuse && texture2D(map_diffuse, v_tex_coord).a <= 0.01) {
+ discard;
+ }
+ }
+ """ @ glsl_fragment_shader
+
+ # Vertices coordinates
+ var coord = attributes["coord"].as(AttributeVec4) is lazy
+
+ # Should this program use the texture `map_diffuse`?
+ var use_map_diffuse = uniforms["use_map_diffuse"].as(UniformBool) is lazy
+
+ # Diffuse texture unit
+ var map_diffuse = uniforms["map_diffuse"].as(UniformSampler2D) is lazy
+
+ # Coordinates on the textures, per vertex
+ var tex_coord = attributes["tex_coord"].as(AttributeVec2) is lazy
+
+ # Diffuse color
+ var diffuse_color = uniforms["diffuse_color"].as(UniformVec4) is lazy
+
+ # Translation applied to each vertex
+ var translation = uniforms["translation"].as(UniformVec4) is lazy
+
+ # Rotation matrix
+ var rotation = uniforms["rotation"].as(UniformMat4) is lazy
+
+ # Scaling per vertex
+ var scale = uniforms["scale"].as(UniformFloat) is lazy
+
+ # Model view projection matrix
+ var mvp = uniforms["mvp"].as(UniformMat4) is lazy
+end
+
+# Draw the camera point of view on screen
+private class LightPointOfViewProgram
+ super GamnitProgramFromSource
+
+ redef var vertex_shader_source = """
+ // Vertex coordinates
+ attribute vec3 coord;
+
+ // Vertex coordinates on textures
+ attribute vec2 tex_coord;
+
+ // Output to the fragment shader
+ varying vec2 v_coord;
+
+ void main()
+ {
+ gl_Position = vec4(coord, 1.0);
+ v_coord = tex_coord;
+ }
+ """ @ glsl_vertex_shader
+
+ redef var fragment_shader_source = """
+ precision mediump float;
+
+ // Virtual screen texture / color attachment
+ uniform sampler2D texture0;
+
+ // Input from the vertex shader
+ varying vec2 v_coord;
+
+ void main()
+ {
+ gl_FragColor = texture2D(texture0, v_coord);
+ }
+ """ @ glsl_fragment_shader
+
+ # Vertices coordinates
+ var coord = attributes["coord"].as(AttributeVec3) is lazy
+
+ # Coordinates on the textures, per vertex
+ var tex_coord = attributes["tex_coord"].as(AttributeVec2) is lazy
+
+ # Visible texture
+ var texture = uniforms["texture0"].as(UniformSampler2D) is lazy
+end
#
# The implementation varies per platform.
fun feed_events do end
+
+ # Extensions to OpenGL ES 2.0 supported by the current configuration
+ var gl_extensions: Array[String] is lazy do return glGetString(gl_EXTENSIONS).split(' ')
end
# Audio support
var inited = mix.initialize(mix_init_flags)
- assert inited != mix_init_flags else
+ if inited != mix_init_flags then
print_error "Failed to load SDL2 mixer format supports: {mix.error}"
end
- var opened = mix.open_audio(44100, mix.default_format, 2, 1024)
- assert opened else
+ var open = mix.open_audio(44100, mix.default_format, 2, 1024)
+ if not open then
print_error "Failed to initialize SDL2 mixer: {mix.error}"
end
# SDL2 mixer initialization flags
#
- # Defaults to all available formats.
- var mix_init_flags: MixInitFlags = mix.flac | mix.mod | mix.mp3 | mix.ogg is lazy, writable
+ # Defaults to FLAC, MP3 and OGG.
+ var mix_init_flags: MixInitFlags = mix.flac | mix.mp3 | mix.ogg is lazy, writable
# Close the SDL display
fun close_sdl
# Draw
glDrawArrays(gl_TRIANGLE_STRIP, 0, 4)
+ gl_error = glGetError
+ assert gl_error == gl_NO_ERROR else print_error gl_error
+ # Take down
+ glBindBuffer(gl_ARRAY_BUFFER, 0)
gl_error = glGetError
assert gl_error == gl_NO_ERROR else print_error gl_error
end
end
-# Program drawing the dynamic screen to the real screen
+# Handles to reused GL buffers and texture
private class DynamicContext
# Real screen framebuffer
# Buffer name for vertex data
var buffer_array: Int = -1
- var float_per_vertex: Int is lazy do return 4 + 4 + 3
-
# Prepare all attributes once per resolution change
fun prepare_once(display: GamnitDisplay, max_dynamic_resolution_ratio: Float)
do
# Depth
glBindRenderbuffer(gl_RENDERBUFFER, depthbuffer)
assert glIsRenderbuffer(depthbuffer)
- glRenderbufferStorage(gl_RENDERBUFFER, gl_DEPTH_COMPNENT16, width, height)
+ glRenderbufferStorage(gl_RENDERBUFFER, gl_DEPTH_COMPONENT16, width, height)
glFramebufferRenderbuffer(gl_FRAMEBUFFER, gl_DEPTH_ATTACHMENT, gl_RENDERBUFFER, depthbuffer)
var gl_error = glGetError
assert gl_error == gl_NO_ERROR else print_error gl_error
var display = new GamnitDisplay
display.setup
self.display = display
+
+ # Print the current GL configuration, for debugging
+ print "GL vendor: {glGetString(gl_VENDOR)}"
+ print "GL renderer: {glGetString(gl_RENDERER)}"
+ print "GL version: {glGetString(gl_VERSION)}"
+ print "GLSL version: {glGetString(gl_SHADING_LANGUAGE_VERSION)}"
+ print "GL extensions: {glGetString(gl_EXTENSIONS)}"
end
# Core of the frame logic, executed only when the display is visible
fun uniform(val: Bool) do uniform_1i(location, if val then 1 else 0)
end
+# Shader uniform of GLSL type `int`
+class UniformInt
+ super Uniform
+
+ # Set this uniform value
+ fun uniform(val: Int) do uniform_1i(location, val)
+end
+
# Shader uniform of GLSL type `vec4`
class UniformFloat
super Uniform
class InactiveUniform
super InactiveVariable
super UniformBool
+ super UniformInt
super UniformFloat
super UniformSampler2D
super UniformVec2
var uniform
if typ == gl_BOOL then
uniform = new UniformBool(gl_program, name, location, size)
+ else if typ == gl_INT then
+ uniform = new UniformInt(gl_program, name, location, size)
else if typ == gl_SAMPLER_2D then
uniform = new UniformSampler2D(gl_program, name, location, size)
else if typ == gl_FLOAT then
fun gl_RGB5_A1: GLRenderbufferFormat `{ return GL_RGB5_A1; `}
# 16 depth bits format
-fun gl_DEPTH_COMPNENT16: GLRenderbufferFormat `{ return GL_DEPTH_COMPONENT16; `}
+fun gl_DEPTH_COMPONENT16: GLRenderbufferFormat `{ return GL_DEPTH_COMPONENT16; `}
# 8 stencil bits format
fun gl_STENCIL_INDEX8: GLRenderbufferFormat `{ return GL_STENCIL_INDEX8; `}
fun gl_ALPHA: GLPixelFormat `{ return GL_ALPHA; `}
fun gl_RGB: GLPixelFormat `{ return GL_RGB; `}
fun gl_RGBA: GLPixelFormat `{ return GL_RGBA; `}
+fun gl_DEPTH_COMPONENT: GLPixelFormat `{ return GL_DEPTH_COMPONENT; `}
# Set of buffers as a bitwise OR mask
extern class GLBuffer `{ GLbitfield `}
fun gl_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING: GLGetParameterName `{ return GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING; `}
fun gl_FRAMEBUFFER_BINDING: GLGetParameterName `{ return GL_FRAMEBUFFER_BINDING; `}
fun gl_RENDERBUFFER_BINDING: GLGetParameterName `{ return GL_RENDERBUFFER_BINDING; `}
+
+# Return a string describing the current GL configuration
+fun glGetString(name: GLEnum): String do return glGetString_native(name).to_s
+private fun glGetString_native(name: GLEnum): CString `{ return (char*)glGetString(name); `}
+
+# Company responsible for this GL implementation
+fun gl_VENDOR: GLEnum `{ return GL_VENDOR; `}
+
+# Name of the renderer, typically specific to a particular configuration of the hardware platform
+fun gl_RENDERER: GLEnum `{ return GL_RENDERER; `}
+
+# Version or release number
+fun gl_VERSION: GLEnum `{ return GL_VERSION; `}
+
+# Version or release number for the shading language of the form
+fun gl_SHADING_LANGUAGE_VERSION: GLEnum `{ return GL_SHADING_LANGUAGE_VERSION; `}
+
+# Space-separated list of supported extensions to GL
+fun gl_EXTENSIONS: GLEnum `{ return GL_EXTENSIONS; `}
+++ /dev/null
-json_lexer.nit -diff
-json_parser.nit -diff
+++ /dev/null
-json.concrete_grammar.txt
-json.dfa.dot
-json.gram.dot
-json.lr.dot
-json.lr.txt
-json.nfa.dot
-json_test_parser.nit
+++ /dev/null
-NITCCDIR=../../contrib/nitcc/
-
-pre-build: $(NITCCDIR)src/nitcc
- $(NITCCDIR)src/nitcc $(NITCCDIR)examples/json.sablecc
-
-$(NITCCDIR)src/nitcc:
- make -C $(NITCCDIR)
# You are allowed to redistribute it and sell it, alone or is a part of
# another product.
-# Errors related to JSON parsing.
-module json::error
+# Intro `JsonParseError` which is exposed by all JSON reading APIs
+module error
-import nitcc_runtime
+import parser_base
-# Ill-formed JSON.
+# JSON format error at parsing
class JsonParseError
super Error
serialize
- # The location of the error in the original text.
- var position: nullable Position
-
- redef fun to_s do
- var p = position
- if p isa Position then
- return "Error Parsing JSON: [{p}] {super}"
- else
- return super
- end
- end
+ # Location of the error in source
+ var location: nullable Location = null
end
+++ /dev/null
-# Lexer generated by nitcc for the grammar json
-module json_lexer is generated, no_warning "missing-doc"
-import nitcc_runtime
-import json_parser
-class Lexer_json
- super Lexer
- redef fun start_state do return dfastate_0
-end
-private fun dfastate_0: DFAState0 do return once new DFAState0
-private fun dfastate_1: DFAState1 do return once new DFAState1
-private fun dfastate_2: DFAState2 do return once new DFAState2
-private fun dfastate_3: DFAState3 do return once new DFAState3
-private fun dfastate_4: DFAState4 do return once new DFAState4
-private fun dfastate_5: DFAState5 do return once new DFAState5
-private fun dfastate_6: DFAState6 do return once new DFAState6
-private fun dfastate_7: DFAState7 do return once new DFAState7
-private fun dfastate_8: DFAState8 do return once new DFAState8
-private fun dfastate_9: DFAState9 do return once new DFAState9
-private fun dfastate_10: DFAState10 do return once new DFAState10
-private fun dfastate_11: DFAState11 do return once new DFAState11
-private fun dfastate_12: DFAState12 do return once new DFAState12
-private fun dfastate_13: DFAState13 do return once new DFAState13
-private fun dfastate_14: DFAState14 do return once new DFAState14
-private fun dfastate_15: DFAState15 do return once new DFAState15
-private fun dfastate_16: DFAState16 do return once new DFAState16
-private fun dfastate_17: DFAState17 do return once new DFAState17
-private fun dfastate_18: DFAState18 do return once new DFAState18
-private fun dfastate_19: DFAState19 do return once new DFAState19
-private fun dfastate_20: DFAState20 do return once new DFAState20
-private fun dfastate_21: DFAState21 do return once new DFAState21
-private fun dfastate_22: DFAState22 do return once new DFAState22
-private fun dfastate_23: DFAState23 do return once new DFAState23
-private fun dfastate_24: DFAState24 do return once new DFAState24
-private fun dfastate_25: DFAState25 do return once new DFAState25
-private fun dfastate_26: DFAState26 do return once new DFAState26
-private fun dfastate_27: DFAState27 do return once new DFAState27
-private fun dfastate_28: DFAState28 do return once new DFAState28
-private fun dfastate_29: DFAState29 do return once new DFAState29
-private fun dfastate_30: DFAState30 do return once new DFAState30
-private fun dfastate_31: DFAState31 do return once new DFAState31
-private fun dfastate_32: DFAState32 do return once new DFAState32
-private fun dfastate_33: DFAState33 do return once new DFAState33
-private fun dfastate_34: DFAState34 do return once new DFAState34
-class MyNToken
- super NToken
-end
-private class DFAState0
- super DFAState
- redef fun trans(char) do
- var c = char.code_point
- if c <= 8 then return null
- if c <= 10 then return dfastate_1
- if c <= 31 then return null
- if c <= 32 then return dfastate_1
- if c <= 33 then return null
- if c <= 34 then return dfastate_2
- if c <= 43 then return null
- if c <= 44 then return dfastate_3
- if c <= 45 then return dfastate_4
- if c <= 47 then return null
- if c <= 57 then return dfastate_5
- if c <= 58 then return dfastate_6
- if c <= 90 then return null
- if c <= 91 then return dfastate_7
- if c <= 92 then return null
- if c <= 93 then return dfastate_8
- if c <= 101 then return null
- if c <= 102 then return dfastate_9
- if c <= 109 then return null
- if c <= 110 then return dfastate_10
- if c <= 115 then return null
- if c <= 116 then return dfastate_11
- if c <= 122 then return null
- if c <= 123 then return dfastate_12
- if c <= 124 then return null
- if c <= 125 then return dfastate_13
- return null
- end
-end
-private class DFAState1
- super DFAState
- redef fun is_accept do return true
- redef fun is_ignored do return true
- redef fun make_token(position, source) do
- return null
- end
- redef fun trans(char) do
- var c = char.code_point
- if c <= 8 then return null
- if c <= 10 then return dfastate_1
- if c <= 31 then return null
- if c <= 32 then return dfastate_1
- return null
- end
-end
-private class DFAState2
- super DFAState
- redef fun trans(char) do
- var c = char.code_point
- if c > 92 then return dfastate_2
- if c <= 33 then return dfastate_2
- if c <= 34 then return dfastate_29
- if c <= 91 then return dfastate_2
- return dfastate_30
- end
-end
-private class DFAState3
- super DFAState
- redef fun is_accept do return true
- redef fun make_token(position, source) do
- var t = new N_39d_44d_39d
- t.text = ","
- t.position = position
- return t
- end
-end
-private class DFAState4
- super DFAState
- redef fun trans(char) do
- var c = char.code_point
- if c <= 47 then return null
- if c <= 57 then return dfastate_5
- return null
- end
-end
-private class DFAState5
- super DFAState
- redef fun is_accept do return true
- redef fun make_token(position, source) do
- var t = new Nnumber
- t.text = position.extract(source)
- t.position = position
- return t
- end
- redef fun trans(char) do
- var c = char.code_point
- if c <= 45 then return null
- if c <= 46 then return dfastate_24
- if c <= 47 then return null
- if c <= 57 then return dfastate_5
- if c <= 68 then return null
- if c <= 69 then return dfastate_25
- if c <= 100 then return null
- if c <= 101 then return dfastate_25
- return null
- end
-end
-private class DFAState6
- super DFAState
- redef fun is_accept do return true
- redef fun make_token(position, source) do
- var t = new N_39d_58d_39d
- t.text = ":"
- t.position = position
- return t
- end
-end
-private class DFAState7
- super DFAState
- redef fun is_accept do return true
- redef fun make_token(position, source) do
- var t = new N_39d_91d_39d
- t.text = "["
- t.position = position
- return t
- end
-end
-private class DFAState8
- super DFAState
- redef fun is_accept do return true
- redef fun make_token(position, source) do
- var t = new N_39d_93d_39d
- t.text = "]"
- t.position = position
- return t
- end
-end
-private class DFAState9
- super DFAState
- redef fun trans(char) do
- var c = char.code_point
- if c <= 96 then return null
- if c <= 97 then return dfastate_20
- return null
- end
-end
-private class DFAState10
- super DFAState
- redef fun trans(char) do
- var c = char.code_point
- if c <= 116 then return null
- if c <= 117 then return dfastate_17
- return null
- end
-end
-private class DFAState11
- super DFAState
- redef fun trans(char) do
- var c = char.code_point
- if c <= 113 then return null
- if c <= 114 then return dfastate_14
- return null
- end
-end
-private class DFAState12
- super DFAState
- redef fun is_accept do return true
- redef fun make_token(position, source) do
- var t = new N_39d_123d_39d
- t.text = "\{"
- t.position = position
- return t
- end
-end
-private class DFAState13
- super DFAState
- redef fun is_accept do return true
- redef fun make_token(position, source) do
- var t = new N_39d_125d_39d
- t.text = "\}"
- t.position = position
- return t
- end
-end
-private class DFAState14
- super DFAState
- redef fun trans(char) do
- var c = char.code_point
- if c <= 116 then return null
- if c <= 117 then return dfastate_15
- return null
- end
-end
-private class DFAState15
- super DFAState
- redef fun trans(char) do
- var c = char.code_point
- if c <= 100 then return null
- if c <= 101 then return dfastate_16
- return null
- end
-end
-private class DFAState16
- super DFAState
- redef fun is_accept do return true
- redef fun make_token(position, source) do
- var t = new N_39dtrue_39d
- t.text = "true"
- t.position = position
- return t
- end
-end
-private class DFAState17
- super DFAState
- redef fun trans(char) do
- var c = char.code_point
- if c <= 107 then return null
- if c <= 108 then return dfastate_18
- return null
- end
-end
-private class DFAState18
- super DFAState
- redef fun trans(char) do
- var c = char.code_point
- if c <= 107 then return null
- if c <= 108 then return dfastate_19
- return null
- end
-end
-private class DFAState19
- super DFAState
- redef fun is_accept do return true
- redef fun make_token(position, source) do
- var t = new N_39dnull_39d
- t.text = "null"
- t.position = position
- return t
- end
-end
-private class DFAState20
- super DFAState
- redef fun trans(char) do
- var c = char.code_point
- if c <= 107 then return null
- if c <= 108 then return dfastate_21
- return null
- end
-end
-private class DFAState21
- super DFAState
- redef fun trans(char) do
- var c = char.code_point
- if c <= 114 then return null
- if c <= 115 then return dfastate_22
- return null
- end
-end
-private class DFAState22
- super DFAState
- redef fun trans(char) do
- var c = char.code_point
- if c <= 100 then return null
- if c <= 101 then return dfastate_23
- return null
- end
-end
-private class DFAState23
- super DFAState
- redef fun is_accept do return true
- redef fun make_token(position, source) do
- var t = new N_39dfalse_39d
- t.text = "false"
- t.position = position
- return t
- end
-end
-private class DFAState24
- super DFAState
- redef fun trans(char) do
- var c = char.code_point
- if c <= 47 then return null
- if c <= 57 then return dfastate_28
- return null
- end
-end
-private class DFAState25
- super DFAState
- redef fun trans(char) do
- var c = char.code_point
- if c <= 42 then return null
- if c <= 43 then return dfastate_26
- if c <= 44 then return null
- if c <= 45 then return dfastate_26
- if c <= 47 then return null
- if c <= 57 then return dfastate_27
- return null
- end
-end
-private class DFAState26
- super DFAState
- redef fun trans(char) do
- var c = char.code_point
- if c <= 47 then return null
- if c <= 57 then return dfastate_27
- return null
- end
-end
-private class DFAState27
- super DFAState
- redef fun is_accept do return true
- redef fun make_token(position, source) do
- var t = new Nnumber
- t.text = position.extract(source)
- t.position = position
- return t
- end
- redef fun trans(char) do
- var c = char.code_point
- if c <= 47 then return null
- if c <= 57 then return dfastate_27
- return null
- end
-end
-private class DFAState28
- super DFAState
- redef fun is_accept do return true
- redef fun make_token(position, source) do
- var t = new Nnumber
- t.text = position.extract(source)
- t.position = position
- return t
- end
- redef fun trans(char) do
- var c = char.code_point
- if c <= 47 then return null
- if c <= 57 then return dfastate_28
- if c <= 68 then return null
- if c <= 69 then return dfastate_25
- if c <= 100 then return null
- if c <= 101 then return dfastate_25
- return null
- end
-end
-private class DFAState29
- super DFAState
- redef fun is_accept do return true
- redef fun make_token(position, source) do
- var t = new Nstring
- t.text = position.extract(source)
- t.position = position
- return t
- end
-end
-private class DFAState30
- super DFAState
- redef fun trans(char) do
- var c = char.code_point
- if c <= 33 then return null
- if c <= 34 then return dfastate_2
- if c <= 46 then return null
- if c <= 47 then return dfastate_2
- if c <= 91 then return null
- if c <= 92 then return dfastate_2
- if c <= 97 then return null
- if c <= 98 then return dfastate_2
- if c <= 101 then return null
- if c <= 102 then return dfastate_2
- if c <= 109 then return null
- if c <= 110 then return dfastate_2
- if c <= 113 then return null
- if c <= 114 then return dfastate_2
- if c <= 115 then return null
- if c <= 116 then return dfastate_2
- if c <= 117 then return dfastate_31
- return null
- end
-end
-private class DFAState31
- super DFAState
- redef fun trans(char) do
- var c = char.code_point
- if c <= 47 then return null
- if c <= 57 then return dfastate_32
- if c <= 64 then return null
- if c <= 90 then return dfastate_32
- if c <= 96 then return null
- if c <= 122 then return dfastate_32
- return null
- end
-end
-private class DFAState32
- super DFAState
- redef fun trans(char) do
- var c = char.code_point
- if c <= 47 then return null
- if c <= 57 then return dfastate_33
- if c <= 64 then return null
- if c <= 90 then return dfastate_33
- if c <= 96 then return null
- if c <= 122 then return dfastate_33
- return null
- end
-end
-private class DFAState33
- super DFAState
- redef fun trans(char) do
- var c = char.code_point
- if c <= 47 then return null
- if c <= 57 then return dfastate_34
- if c <= 64 then return null
- if c <= 90 then return dfastate_34
- if c <= 96 then return null
- if c <= 122 then return dfastate_34
- return null
- end
-end
-private class DFAState34
- super DFAState
- redef fun trans(char) do
- var c = char.code_point
- if c <= 47 then return null
- if c <= 57 then return dfastate_2
- if c <= 64 then return null
- if c <= 90 then return dfastate_2
- if c <= 96 then return null
- if c <= 122 then return dfastate_2
- return null
- end
-end
+++ /dev/null
-# Parser generated by nitcc for the grammar json
-module json_parser is generated, no_warning("missing-doc","old-init")
-import nitcc_runtime
-class Parser_json
- super Parser
- redef fun start_state do return state_Start
-end
-private fun state_Start: LRStateStart do return once new LRStateStart
-private fun state_value: LRStatevalue do return once new LRStatevalue
-private fun state_number: LRStatenumber do return once new LRStatenumber
-private fun state_string: LRStatestring do return once new LRStatestring
-private fun state__39dtrue_39d: LRState_39dtrue_39d do return once new LRState_39dtrue_39d
-private fun state__39dfalse_39d: LRState_39dfalse_39d do return once new LRState_39dfalse_39d
-private fun state__39dnull_39d: LRState_39dnull_39d do return once new LRState_39dnull_39d
-private fun state__39d_123d_39d: LRState_39d_123d_39d do return once new LRState_39d_123d_39d
-private fun state__39d_91d_39d: LRState_39d_91d_39d do return once new LRState_39d_91d_39d
-private fun state_value_32dEof: LRStatevalue_32dEof do return once new LRStatevalue_32dEof
-private fun state__39d_123d_39d_32dmembers: LRState_39d_123d_39d_32dmembers do return once new LRState_39d_123d_39d_32dmembers
-private fun state__39d_123d_39d_32d_39d_125d_39d: LRState_39d_123d_39d_32d_39d_125d_39d do return once new LRState_39d_123d_39d_32d_39d_125d_39d
-private fun state__39d_123d_39d_32dpair: LRState_39d_123d_39d_32dpair do return once new LRState_39d_123d_39d_32dpair
-private fun state__39d_123d_39d_32dstring: LRState_39d_123d_39d_32dstring do return once new LRState_39d_123d_39d_32dstring
-private fun state__39d_91d_39d_32delements: LRState_39d_91d_39d_32delements do return once new LRState_39d_91d_39d_32delements
-private fun state__39d_91d_39d_32d_39d_93d_39d: LRState_39d_91d_39d_32d_39d_93d_39d do return once new LRState_39d_91d_39d_32d_39d_93d_39d
-private fun state__39d_91d_39d_32dvalue: LRState_39d_91d_39d_32dvalue do return once new LRState_39d_91d_39d_32dvalue
-private fun state__39d_123d_39d_32dmembers_32d_39d_125d_39d: LRState_39d_123d_39d_32dmembers_32d_39d_125d_39d do return once new LRState_39d_123d_39d_32dmembers_32d_39d_125d_39d
-private fun state__39d_123d_39d_32dmembers_32d_39d_44d_39d: LRState_39d_123d_39d_32dmembers_32d_39d_44d_39d do return once new LRState_39d_123d_39d_32dmembers_32d_39d_44d_39d
-private fun state__39d_123d_39d_32dstring_32d_39d_58d_39d: LRState_39d_123d_39d_32dstring_32d_39d_58d_39d do return once new LRState_39d_123d_39d_32dstring_32d_39d_58d_39d
-private fun state__39d_91d_39d_32delements_32d_39d_93d_39d: LRState_39d_91d_39d_32delements_32d_39d_93d_39d do return once new LRState_39d_91d_39d_32delements_32d_39d_93d_39d
-private fun state__39d_91d_39d_32delements_32d_39d_44d_39d: LRState_39d_91d_39d_32delements_32d_39d_44d_39d do return once new LRState_39d_91d_39d_32delements_32d_39d_44d_39d
-private fun state__39d_123d_39d_32dmembers_32d_39d_44d_39d_32dpair: LRState_39d_123d_39d_32dmembers_32d_39d_44d_39d_32dpair do return once new LRState_39d_123d_39d_32dmembers_32d_39d_44d_39d_32dpair
-private fun state__39d_123d_39d_32dstring_32d_39d_58d_39d_32dvalue: LRState_39d_123d_39d_32dstring_32d_39d_58d_39d_32dvalue do return once new LRState_39d_123d_39d_32dstring_32d_39d_58d_39d_32dvalue
-private fun state__39d_91d_39d_32delements_32d_39d_44d_39d_32dvalue: LRState_39d_91d_39d_32delements_32d_39d_44d_39d_32dvalue do return once new LRState_39d_91d_39d_32delements_32d_39d_44d_39d_32dvalue
-private fun goto_Nvalue: Goto_Nvalue do return once new Goto_Nvalue
-private fun reduce_Nvalue_number(parser: Parser) do
- # REDUCE value::value_number=number
- var n0 = parser.pop.as(Nnumber)
- var p1 = new Nvalue_number(n0)
- var prod = p1
- parser.node_stack.push prod
- parser.goto(goto_Nvalue)
-end
-private fun reduce_Nvalue_string(parser: Parser) do
- # REDUCE value::value_string=string
- var n0 = parser.pop.as(Nstring)
- var p1 = new Nvalue_string(n0)
- var prod = p1
- parser.node_stack.push prod
- parser.goto(goto_Nvalue)
-end
-private fun reduce_Nvalue_true(parser: Parser) do
- # REDUCE value::value_true='true'
- var n0 = parser.pop.as(N_39dtrue_39d)
- var p1 = new Nvalue_true(n0)
- var prod = p1
- parser.node_stack.push prod
- parser.goto(goto_Nvalue)
-end
-private fun reduce_Nvalue_false(parser: Parser) do
- # REDUCE value::value_false='false'
- var n0 = parser.pop.as(N_39dfalse_39d)
- var p1 = new Nvalue_false(n0)
- var prod = p1
- parser.node_stack.push prod
- parser.goto(goto_Nvalue)
-end
-private fun reduce_Nvalue_null(parser: Parser) do
- # REDUCE value::value_null='null'
- var n0 = parser.pop.as(N_39dnull_39d)
- var p1 = new Nvalue_null(n0)
- var prod = p1
- parser.node_stack.push prod
- parser.goto(goto_Nvalue)
-end
-private fun reduce_Nvalue_object_95d0(parser: Parser) do
- # REDUCE value::value_object_0='{' members '}'
- var n2 = parser.pop.as(N_39d_125d_39d)
- var n1 = parser.pop.as(Nmembers)
- var n0 = parser.pop.as(N_39d_123d_39d)
- var p1 = new Nvalue_object(n0, n1, n2)
- var prod = p1
- parser.node_stack.push prod
- parser.goto(goto_Nvalue)
-end
-private fun reduce_Nvalue_object_95d1(parser: Parser) do
- # REDUCE value::value_object_1='{' '}'
- var n1 = parser.pop.as(N_39d_125d_39d)
- var n0 = parser.pop.as(N_39d_123d_39d)
- var p1 = new Nvalue_object(n0, null, n1)
- var prod = p1
- parser.node_stack.push prod
- parser.goto(goto_Nvalue)
-end
-private fun reduce_Nvalue_array_95d0(parser: Parser) do
- # REDUCE value::value_array_0='[' elements ']'
- var n2 = parser.pop.as(N_39d_93d_39d)
- var n1 = parser.pop.as(Nelements)
- var n0 = parser.pop.as(N_39d_91d_39d)
- var p1 = new Nvalue_array(n0, n1, n2)
- var prod = p1
- parser.node_stack.push prod
- parser.goto(goto_Nvalue)
-end
-private fun reduce_Nvalue_array_95d1(parser: Parser) do
- # REDUCE value::value_array_1='[' ']'
- var n1 = parser.pop.as(N_39d_93d_39d)
- var n0 = parser.pop.as(N_39d_91d_39d)
- var p1 = new Nvalue_array(n0, null, n1)
- var prod = p1
- parser.node_stack.push prod
- parser.goto(goto_Nvalue)
-end
-private fun goto_Nmembers: Goto_Nmembers do return once new Goto_Nmembers
-private fun reduce_Nmembers_tail(parser: Parser) do
- # REDUCE members::members_tail=members ',' pair
- var n2 = parser.pop.as(Npair)
- var n1 = parser.pop.as(N_39d_44d_39d)
- var n0 = parser.pop.as(Nmembers)
- var p1 = new Nmembers_tail(n0, n1, n2)
- var prod = p1
- parser.node_stack.push prod
- parser.goto(goto_Nmembers)
-end
-private fun reduce_Nmembers_head(parser: Parser) do
- # REDUCE members::members_head=pair
- var n0 = parser.pop.as(Npair)
- var p1 = new Nmembers_head(n0)
- var prod = p1
- parser.node_stack.push prod
- parser.goto(goto_Nmembers)
-end
-private fun goto_Npair: Goto_Npair do return once new Goto_Npair
-private fun reduce_Npair(parser: Parser) do
- # REDUCE pair::pair=string ':' value
- var n2 = parser.pop.as(Nvalue)
- var n1 = parser.pop.as(N_39d_58d_39d)
- var n0 = parser.pop.as(Nstring)
- var p1 = new Npair(n0, n1, n2)
- var prod = p1
- parser.node_stack.push prod
- parser.goto(goto_Npair)
-end
-private fun goto_Nelements: Goto_Nelements do return once new Goto_Nelements
-private fun reduce_Nelements_tail(parser: Parser) do
- # REDUCE elements::elements_tail=elements ',' value
- var n2 = parser.pop.as(Nvalue)
- var n1 = parser.pop.as(N_39d_44d_39d)
- var n0 = parser.pop.as(Nelements)
- var p1 = new Nelements_tail(n0, n1, n2)
- var prod = p1
- parser.node_stack.push prod
- parser.goto(goto_Nelements)
-end
-private fun reduce_Nelements_head(parser: Parser) do
- # REDUCE elements::elements_head=value
- var n0 = parser.pop.as(Nvalue)
- var p1 = new Nelements_head(n0)
- var prod = p1
- parser.node_stack.push prod
- parser.goto(goto_Nelements)
-end
-private fun goto_N_start: Goto_N_start do return once new Goto_N_start
-private fun reduce_NStart(parser: Parser) do
- # REDUCE _start::Start=value Eof
- var n1 = parser.pop.as(NEof)
- var n0 = parser.pop.as(Nvalue)
- var p1 = new NStart(n0, n1)
- var prod = p1
- parser.node_stack.push prod
- parser.stop = true
-end
-redef class NToken
- # guarded action for state Start
- # 7 shift(s) and 0 reduce(s)
- private fun action_sStart(parser: Parser) do
- parser.parse_error
- end
- # guarded action for state value
- # 1 shift(s) and 0 reduce(s)
- private fun action_svalue(parser: Parser) do
- parser.parse_error
- end
- # guarded action for state '{'
- # 2 shift(s) and 0 reduce(s)
- private fun action_s_39d_123d_39d(parser: Parser) do
- parser.parse_error
- end
- # guarded action for state '['
- # 8 shift(s) and 0 reduce(s)
- private fun action_s_39d_91d_39d(parser: Parser) do
- parser.parse_error
- end
- # guarded action for state '{' members
- # 2 shift(s) and 0 reduce(s)
- private fun action_s_39d_123d_39d_32dmembers(parser: Parser) do
- parser.parse_error
- end
- # guarded action for state '{' string
- # 1 shift(s) and 0 reduce(s)
- private fun action_s_39d_123d_39d_32dstring(parser: Parser) do
- parser.parse_error
- end
- # guarded action for state '[' elements
- # 2 shift(s) and 0 reduce(s)
- private fun action_s_39d_91d_39d_32delements(parser: Parser) do
- parser.parse_error
- end
- # guarded action for state '{' members ','
- # 1 shift(s) and 0 reduce(s)
- private fun action_s_39d_123d_39d_32dmembers_32d_39d_44d_39d(parser: Parser) do
- parser.parse_error
- end
- # guarded action for state '{' string ':'
- # 7 shift(s) and 0 reduce(s)
- private fun action_s_39d_123d_39d_32dstring_32d_39d_58d_39d(parser: Parser) do
- parser.parse_error
- end
- # guarded action for state '[' elements ','
- # 7 shift(s) and 0 reduce(s)
- private fun action_s_39d_91d_39d_32delements_32d_39d_44d_39d(parser: Parser) do
- parser.parse_error
- end
-end
-class N_39d_123d_39d
- super NToken
- redef fun action_sStart(parser) do
- parser.shift(state__39d_123d_39d)
- end
- redef fun action_s_39d_91d_39d(parser) do
- parser.shift(state__39d_123d_39d)
- end
- redef fun action_s_39d_123d_39d_32dstring_32d_39d_58d_39d(parser) do
- parser.shift(state__39d_123d_39d)
- end
- redef fun action_s_39d_91d_39d_32delements_32d_39d_44d_39d(parser) do
- parser.shift(state__39d_123d_39d)
- end
- redef fun node_name do return "\'\{\'"
-end
-class N_39d_125d_39d
- super NToken
- redef fun action_s_39d_123d_39d(parser) do
- parser.shift(state__39d_123d_39d_32d_39d_125d_39d)
- end
- redef fun action_s_39d_123d_39d_32dmembers(parser) do
- parser.shift(state__39d_123d_39d_32dmembers_32d_39d_125d_39d)
- end
- redef fun node_name do return "\'\}\'"
-end
-class N_39d_91d_39d
- super NToken
- redef fun action_sStart(parser) do
- parser.shift(state__39d_91d_39d)
- end
- redef fun action_s_39d_91d_39d(parser) do
- parser.shift(state__39d_91d_39d)
- end
- redef fun action_s_39d_123d_39d_32dstring_32d_39d_58d_39d(parser) do
- parser.shift(state__39d_91d_39d)
- end
- redef fun action_s_39d_91d_39d_32delements_32d_39d_44d_39d(parser) do
- parser.shift(state__39d_91d_39d)
- end
- redef fun node_name do return "\'[\'"
-end
-class N_39d_93d_39d
- super NToken
- redef fun action_s_39d_91d_39d(parser) do
- parser.shift(state__39d_91d_39d_32d_39d_93d_39d)
- end
- redef fun action_s_39d_91d_39d_32delements(parser) do
- parser.shift(state__39d_91d_39d_32delements_32d_39d_93d_39d)
- end
- redef fun node_name do return "\']\'"
-end
-class Nnumber
- super NToken
- redef fun action_sStart(parser) do
- parser.shift(state_number)
- end
- redef fun action_s_39d_91d_39d(parser) do
- parser.shift(state_number)
- end
- redef fun action_s_39d_123d_39d_32dstring_32d_39d_58d_39d(parser) do
- parser.shift(state_number)
- end
- redef fun action_s_39d_91d_39d_32delements_32d_39d_44d_39d(parser) do
- parser.shift(state_number)
- end
- redef fun node_name do return "number"
-end
-class Nstring
- super NToken
- redef fun action_sStart(parser) do
- parser.shift(state_string)
- end
- redef fun action_s_39d_123d_39d(parser) do
- parser.shift(state__39d_123d_39d_32dstring)
- end
- redef fun action_s_39d_91d_39d(parser) do
- parser.shift(state_string)
- end
- redef fun action_s_39d_123d_39d_32dmembers_32d_39d_44d_39d(parser) do
- parser.shift(state__39d_123d_39d_32dstring)
- end
- redef fun action_s_39d_123d_39d_32dstring_32d_39d_58d_39d(parser) do
- parser.shift(state_string)
- end
- redef fun action_s_39d_91d_39d_32delements_32d_39d_44d_39d(parser) do
- parser.shift(state_string)
- end
- redef fun node_name do return "string"
-end
-class N_39dtrue_39d
- super NToken
- redef fun action_sStart(parser) do
- parser.shift(state__39dtrue_39d)
- end
- redef fun action_s_39d_91d_39d(parser) do
- parser.shift(state__39dtrue_39d)
- end
- redef fun action_s_39d_123d_39d_32dstring_32d_39d_58d_39d(parser) do
- parser.shift(state__39dtrue_39d)
- end
- redef fun action_s_39d_91d_39d_32delements_32d_39d_44d_39d(parser) do
- parser.shift(state__39dtrue_39d)
- end
- redef fun node_name do return "\'true\'"
-end
-class N_39dfalse_39d
- super NToken
- redef fun action_sStart(parser) do
- parser.shift(state__39dfalse_39d)
- end
- redef fun action_s_39d_91d_39d(parser) do
- parser.shift(state__39dfalse_39d)
- end
- redef fun action_s_39d_123d_39d_32dstring_32d_39d_58d_39d(parser) do
- parser.shift(state__39dfalse_39d)
- end
- redef fun action_s_39d_91d_39d_32delements_32d_39d_44d_39d(parser) do
- parser.shift(state__39dfalse_39d)
- end
- redef fun node_name do return "\'false\'"
-end
-class N_39dnull_39d
- super NToken
- redef fun action_sStart(parser) do
- parser.shift(state__39dnull_39d)
- end
- redef fun action_s_39d_91d_39d(parser) do
- parser.shift(state__39dnull_39d)
- end
- redef fun action_s_39d_123d_39d_32dstring_32d_39d_58d_39d(parser) do
- parser.shift(state__39dnull_39d)
- end
- redef fun action_s_39d_91d_39d_32delements_32d_39d_44d_39d(parser) do
- parser.shift(state__39dnull_39d)
- end
- redef fun node_name do return "\'null\'"
-end
-class N_39d_44d_39d
- super NToken
- redef fun action_s_39d_123d_39d_32dmembers(parser) do
- parser.shift(state__39d_123d_39d_32dmembers_32d_39d_44d_39d)
- end
- redef fun action_s_39d_91d_39d_32delements(parser) do
- parser.shift(state__39d_91d_39d_32delements_32d_39d_44d_39d)
- end
- redef fun node_name do return "\',\'"
-end
-class N_39d_58d_39d
- super NToken
- redef fun action_s_39d_123d_39d_32dstring(parser) do
- parser.shift(state__39d_123d_39d_32dstring_32d_39d_58d_39d)
- end
- redef fun node_name do return "\':\'"
-end
-redef class NEof
- super NToken
- redef fun action_svalue(parser) do
- parser.shift(state_value_32dEof)
- end
- redef fun node_name do return "Eof"
-end
-redef class LRGoto
- private fun goto_s_39d_123d_39d(parser: Parser) do abort
- private fun goto_s_39d_91d_39d(parser: Parser) do abort
-end
-class Goto_Nvalue
- super LRGoto
- redef fun goto_s_39d_91d_39d(parser) do
- parser.push(state__39d_91d_39d_32dvalue)
- end
-end
-class Goto_Nmembers
- super LRGoto
- redef fun goto_s_39d_123d_39d(parser) do
- parser.push(state__39d_123d_39d_32dmembers)
- end
-end
-class Goto_Npair
- super LRGoto
- redef fun goto_s_39d_123d_39d(parser) do
- parser.push(state__39d_123d_39d_32dpair)
- end
-end
-class Goto_Nelements
- super LRGoto
- redef fun goto_s_39d_91d_39d(parser) do
- parser.push(state__39d_91d_39d_32delements)
- end
-end
-class Goto_N_start
- super LRGoto
-end
-class Nvalue
- super NProd
- redef fun node_name do return "value"
-end
-class Nvalue_number
- super Nvalue
- redef fun node_name do return "value_number"
- var n_number: Nnumber
- init(n_number: Nnumber) do
- self.n_number = n_number
- end
- redef fun number_of_children do return 1
- redef fun child(i) do
- if i == 0 then return n_number
- abort
- end
-end
-class Nvalue_string
- super Nvalue
- redef fun node_name do return "value_string"
- var n_string: Nstring
- init(n_string: Nstring) do
- self.n_string = n_string
- end
- redef fun number_of_children do return 1
- redef fun child(i) do
- if i == 0 then return n_string
- abort
- end
-end
-class Nvalue_true
- super Nvalue
- redef fun node_name do return "value_true"
- var n_0: N_39dtrue_39d
- init(n_0: N_39dtrue_39d) do
- self.n_0 = n_0
- end
- redef fun number_of_children do return 1
- redef fun child(i) do
- if i == 0 then return n_0
- abort
- end
-end
-class Nvalue_false
- super Nvalue
- redef fun node_name do return "value_false"
- var n_0: N_39dfalse_39d
- init(n_0: N_39dfalse_39d) do
- self.n_0 = n_0
- end
- redef fun number_of_children do return 1
- redef fun child(i) do
- if i == 0 then return n_0
- abort
- end
-end
-class Nvalue_null
- super Nvalue
- redef fun node_name do return "value_null"
- var n_0: N_39dnull_39d
- init(n_0: N_39dnull_39d) do
- self.n_0 = n_0
- end
- redef fun number_of_children do return 1
- redef fun child(i) do
- if i == 0 then return n_0
- abort
- end
-end
-class Nvalue_object
- super Nvalue
- redef fun node_name do return "value_object"
- var n_0: N_39d_123d_39d
- var n_members: nullable Nmembers
- var n_2: N_39d_125d_39d
- init(n_0: N_39d_123d_39d, n_members: nullable Nmembers, n_2: N_39d_125d_39d) do
- self.n_0 = n_0
- self.n_members = n_members
- self.n_2 = n_2
- end
- redef fun number_of_children do return 3
- redef fun child(i) do
- if i == 0 then return n_0
- if i == 1 then return n_members
- if i == 2 then return n_2
- abort
- end
-end
-class Nvalue_array
- super Nvalue
- redef fun node_name do return "value_array"
- var n_0: N_39d_91d_39d
- var n_elements: nullable Nelements
- var n_2: N_39d_93d_39d
- init(n_0: N_39d_91d_39d, n_elements: nullable Nelements, n_2: N_39d_93d_39d) do
- self.n_0 = n_0
- self.n_elements = n_elements
- self.n_2 = n_2
- end
- redef fun number_of_children do return 3
- redef fun child(i) do
- if i == 0 then return n_0
- if i == 1 then return n_elements
- if i == 2 then return n_2
- abort
- end
-end
-class Nmembers
- super NProd
- redef fun node_name do return "members"
-end
-class Nmembers_tail
- super Nmembers
- redef fun node_name do return "members_tail"
- var n_members: Nmembers
- var n_1: N_39d_44d_39d
- var n_pair: Npair
- init(n_members: Nmembers, n_1: N_39d_44d_39d, n_pair: Npair) do
- self.n_members = n_members
- self.n_1 = n_1
- self.n_pair = n_pair
- end
- redef fun number_of_children do return 3
- redef fun child(i) do
- if i == 0 then return n_members
- if i == 1 then return n_1
- if i == 2 then return n_pair
- abort
- end
-end
-class Nmembers_head
- super Nmembers
- redef fun node_name do return "members_head"
- var n_pair: Npair
- init(n_pair: Npair) do
- self.n_pair = n_pair
- end
- redef fun number_of_children do return 1
- redef fun child(i) do
- if i == 0 then return n_pair
- abort
- end
-end
-class Npair
- super NProd
- redef fun node_name do return "pair"
- var n_string: Nstring
- var n_1: N_39d_58d_39d
- var n_value: Nvalue
- init(n_string: Nstring, n_1: N_39d_58d_39d, n_value: Nvalue) do
- self.n_string = n_string
- self.n_1 = n_1
- self.n_value = n_value
- end
- redef fun number_of_children do return 3
- redef fun child(i) do
- if i == 0 then return n_string
- if i == 1 then return n_1
- if i == 2 then return n_value
- abort
- end
-end
-class Nelements
- super NProd
- redef fun node_name do return "elements"
-end
-class Nelements_tail
- super Nelements
- redef fun node_name do return "elements_tail"
- var n_elements: Nelements
- var n_1: N_39d_44d_39d
- var n_value: Nvalue
- init(n_elements: Nelements, n_1: N_39d_44d_39d, n_value: Nvalue) do
- self.n_elements = n_elements
- self.n_1 = n_1
- self.n_value = n_value
- end
- redef fun number_of_children do return 3
- redef fun child(i) do
- if i == 0 then return n_elements
- if i == 1 then return n_1
- if i == 2 then return n_value
- abort
- end
-end
-class Nelements_head
- super Nelements
- redef fun node_name do return "elements_head"
- var n_value: Nvalue
- init(n_value: Nvalue) do
- self.n_value = n_value
- end
- redef fun number_of_children do return 1
- redef fun child(i) do
- if i == 0 then return n_value
- abort
- end
-end
-class N_start
- super NProd
- redef fun node_name do return "_start"
-end
-class NStart
- super N_start
- redef fun node_name do return "Start"
- var n_0: Nvalue
- var n_1: NEof
- init(n_0: Nvalue, n_1: NEof) do
- self.n_0 = n_0
- self.n_1 = n_1
- end
- redef fun number_of_children do return 2
- redef fun child(i) do
- if i == 0 then return n_0
- if i == 1 then return n_1
- abort
- end
-end
-# State Start
-private class LRStateStart
- super LRState
- redef fun to_s do return "Start"
- redef fun error_msg do return "value"
- redef fun action(parser) do
- parser.peek_token.action_sStart(parser)
- end
- redef fun goto(parser, goto) do
- parser.push(state_value)
- end
-end
-# State value
-private class LRStatevalue
- super LRState
- redef fun to_s do return "value"
- redef fun error_msg do return "Eof"
- redef fun action(parser) do
- parser.peek_token.action_svalue(parser)
- end
-end
-# State number
-private class LRStatenumber
- super LRState
- redef fun to_s do return "number"
- redef fun error_msg do return ""
- redef fun action(parser) do
- reduce_Nvalue_number(parser)
- end
-end
-# State string
-private class LRStatestring
- super LRState
- redef fun to_s do return "string"
- redef fun error_msg do return ""
- redef fun action(parser) do
- reduce_Nvalue_string(parser)
- end
-end
-# State 'true'
-private class LRState_39dtrue_39d
- super LRState
- redef fun to_s do return "\'true\'"
- redef fun error_msg do return ""
- redef fun action(parser) do
- reduce_Nvalue_true(parser)
- end
-end
-# State 'false'
-private class LRState_39dfalse_39d
- super LRState
- redef fun to_s do return "\'false\'"
- redef fun error_msg do return ""
- redef fun action(parser) do
- reduce_Nvalue_false(parser)
- end
-end
-# State 'null'
-private class LRState_39dnull_39d
- super LRState
- redef fun to_s do return "\'null\'"
- redef fun error_msg do return ""
- redef fun action(parser) do
- reduce_Nvalue_null(parser)
- end
-end
-# State '{'
-private class LRState_39d_123d_39d
- super LRState
- redef fun to_s do return "\'\{\'"
- redef fun error_msg do return "members, pair"
- redef fun action(parser) do
- parser.peek_token.action_s_39d_123d_39d(parser)
- end
- redef fun goto(parser, goto) do
- goto.goto_s_39d_123d_39d(parser)
- end
-end
-# State '['
-private class LRState_39d_91d_39d
- super LRState
- redef fun to_s do return "\'[\'"
- redef fun error_msg do return "elements, value"
- redef fun action(parser) do
- parser.peek_token.action_s_39d_91d_39d(parser)
- end
- redef fun goto(parser, goto) do
- goto.goto_s_39d_91d_39d(parser)
- end
-end
-# State value Eof
-private class LRStatevalue_32dEof
- super LRState
- redef fun to_s do return "value Eof"
- redef fun error_msg do return ""
- redef fun action(parser) do
- reduce_NStart(parser)
- end
-end
-# State '{' members
-private class LRState_39d_123d_39d_32dmembers
- super LRState
- redef fun to_s do return "\'\{\' members"
- redef fun error_msg do return "\'\}\', \',\'"
- redef fun action(parser) do
- parser.peek_token.action_s_39d_123d_39d_32dmembers(parser)
- end
-end
-# State '{' '}'
-private class LRState_39d_123d_39d_32d_39d_125d_39d
- super LRState
- redef fun to_s do return "\'\{\' \'\}\'"
- redef fun error_msg do return ""
- redef fun action(parser) do
- reduce_Nvalue_object_95d1(parser)
- end
-end
-# State '{' pair
-private class LRState_39d_123d_39d_32dpair
- super LRState
- redef fun to_s do return "\'\{\' pair"
- redef fun error_msg do return ""
- redef fun action(parser) do
- reduce_Nmembers_head(parser)
- end
-end
-# State '{' string
-private class LRState_39d_123d_39d_32dstring
- super LRState
- redef fun to_s do return "\'\{\' string"
- redef fun error_msg do return "\':\'"
- redef fun action(parser) do
- parser.peek_token.action_s_39d_123d_39d_32dstring(parser)
- end
-end
-# State '[' elements
-private class LRState_39d_91d_39d_32delements
- super LRState
- redef fun to_s do return "\'[\' elements"
- redef fun error_msg do return "\']\', \',\'"
- redef fun action(parser) do
- parser.peek_token.action_s_39d_91d_39d_32delements(parser)
- end
-end
-# State '[' ']'
-private class LRState_39d_91d_39d_32d_39d_93d_39d
- super LRState
- redef fun to_s do return "\'[\' \']\'"
- redef fun error_msg do return ""
- redef fun action(parser) do
- reduce_Nvalue_array_95d1(parser)
- end
-end
-# State '[' value
-private class LRState_39d_91d_39d_32dvalue
- super LRState
- redef fun to_s do return "\'[\' value"
- redef fun error_msg do return ""
- redef fun action(parser) do
- reduce_Nelements_head(parser)
- end
-end
-# State '{' members '}'
-private class LRState_39d_123d_39d_32dmembers_32d_39d_125d_39d
- super LRState
- redef fun to_s do return "\'\{\' members \'\}\'"
- redef fun error_msg do return ""
- redef fun action(parser) do
- reduce_Nvalue_object_95d0(parser)
- end
-end
-# State '{' members ','
-private class LRState_39d_123d_39d_32dmembers_32d_39d_44d_39d
- super LRState
- redef fun to_s do return "\'\{\' members \',\'"
- redef fun error_msg do return "pair"
- redef fun action(parser) do
- parser.peek_token.action_s_39d_123d_39d_32dmembers_32d_39d_44d_39d(parser)
- end
- redef fun goto(parser, goto) do
- parser.push(state__39d_123d_39d_32dmembers_32d_39d_44d_39d_32dpair)
- end
-end
-# State '{' string ':'
-private class LRState_39d_123d_39d_32dstring_32d_39d_58d_39d
- super LRState
- redef fun to_s do return "\'\{\' string \':\'"
- redef fun error_msg do return "value"
- redef fun action(parser) do
- parser.peek_token.action_s_39d_123d_39d_32dstring_32d_39d_58d_39d(parser)
- end
- redef fun goto(parser, goto) do
- parser.push(state__39d_123d_39d_32dstring_32d_39d_58d_39d_32dvalue)
- end
-end
-# State '[' elements ']'
-private class LRState_39d_91d_39d_32delements_32d_39d_93d_39d
- super LRState
- redef fun to_s do return "\'[\' elements \']\'"
- redef fun error_msg do return ""
- redef fun action(parser) do
- reduce_Nvalue_array_95d0(parser)
- end
-end
-# State '[' elements ','
-private class LRState_39d_91d_39d_32delements_32d_39d_44d_39d
- super LRState
- redef fun to_s do return "\'[\' elements \',\'"
- redef fun error_msg do return "value"
- redef fun action(parser) do
- parser.peek_token.action_s_39d_91d_39d_32delements_32d_39d_44d_39d(parser)
- end
- redef fun goto(parser, goto) do
- parser.push(state__39d_91d_39d_32delements_32d_39d_44d_39d_32dvalue)
- end
-end
-# State '{' members ',' pair
-private class LRState_39d_123d_39d_32dmembers_32d_39d_44d_39d_32dpair
- super LRState
- redef fun to_s do return "\'\{\' members \',\' pair"
- redef fun error_msg do return ""
- redef fun action(parser) do
- reduce_Nmembers_tail(parser)
- end
-end
-# State '{' string ':' value
-private class LRState_39d_123d_39d_32dstring_32d_39d_58d_39d_32dvalue
- super LRState
- redef fun to_s do return "\'\{\' string \':\' value"
- redef fun error_msg do return ""
- redef fun action(parser) do
- reduce_Npair(parser)
- end
-end
-# State '[' elements ',' value
-private class LRState_39d_91d_39d_32delements_32d_39d_44d_39d_32dvalue
- super LRState
- redef fun to_s do return "\'[\' elements \',\' value"
- redef fun error_msg do return ""
- redef fun action(parser) do
- reduce_Nelements_tail(parser)
- end
-end
# Services to read JSON: `from_json_string` and `JsonDeserializer`
module serialization_read
-import ::serialization::caching
-private import ::serialization::engine_tools
+import serialization::caching
+private import serialization::engine_tools
+import serialization::safe
+
private import static
-private import string_parser
import poset
# Deserializer from a Json string.
class JsonDeserializer
super CachingDeserializer
+ super SafeDeserializer
# Json text to deserialize from.
private var text: Text
- # Accepted parameterized classes to deserialize
- #
- # If `whitelist.empty`, all types are accepted.
- #
- # ~~~nitish
- # import json::serialization
- #
- # class MyClass
- # serialize
- # end
- #
- # var json_string = """
- # {"__class" = "MyClass"}
- # """
- #
- # var deserializer = new JsonDeserializer(json_string)
- # var obj = deserializer.deserialize
- # assert deserializer.errors.is_empty
- # assert obj isa MyClass
- #
- # deserializer = new JsonDeserializer(json_string)
- # deserializer.whitelist.add "Array[String]"
- # deserializer.whitelist.add "AnotherAcceptedClass"
- # obj = deserializer.deserialize
- # assert deserializer.errors.length == 1
- # assert obj == null
- # ~~~
- var whitelist = new Array[Text]
-
- # Should objects be checked if they a subtype of the static type before deserialization?
- #
- # Defaults to `true`, as it should always be activated.
- # It can be turned off to implement the subtype check itself.
- var check_subtypes = true is writable
-
# Root json object parsed from input text.
private var root: nullable Object is noinit
if class_name == null and static_type != null then
# Fallack to the static type, strip the `nullable` prefix
- var prefix = "nullable "
- if static_type.has_prefix(prefix) then
- class_name = static_type.substring_from(prefix.length)
- else class_name = static_type
+ class_name = static_type.strip_nullable
end
end
return object
end
- if whitelist.not_empty and not whitelist.has(class_name) then
- errors.add new Error("Deserialization Error: '{class_name}' not in whitelist")
- return null
- end
-
- if static_type != null and check_subtypes then
- var static_class = static_type.strip_nullable_and_params
- var dynamic_class = class_name.strip_nullable_and_params
- if not class_inheritance_metamodel.has_edge(dynamic_class, static_class) then
- errors.add new Error("Deserialization Error: `{class_name}` is not a subtype of the static type `{static_type}`")
- return null
- end
- end
+ if not accept(class_name, static_type) then return null
# advance on path
path.push object
return val.chars.first
end
+ # byte?
+ if kind == "byte" then
+ var val = object.get_or_null("__val")
+ if not val isa Int then
+ errors.add new Error("Serialization Error: JSON `byte` object does not declare an integer `__val`.")
+ return object
+ end
+
+ return val.to_b
+ end
+
errors.add new Error("Deserialization Error: JSON object has an unknown `__kind`.")
return object
end
if object isa Array[nullable Object] then
# Can we use the static type?
if static_type != null then
- var prefix = "nullable "
- var class_name = if static_type.has(prefix) then
- static_type.substring_from(prefix.length)
- else static_type
-
opened_array = object
- var value = deserialize_class(class_name)
+ var value = deserialize_class(static_type.strip_nullable)
opened_array = null
return value
end
return array
end
+ if object isa String and object.length == 1 and static_type == "Char" then
+ # Char serialized as a JSON string
+ return object.chars.first
+ end
+
+ if object isa Int and static_type == "Byte" then
+ # Byte serialized as an integer
+ return object.to_b
+ end
+
return object
end
end
return res
end
-
- # Strip the `nullable` prefix and the params from the class name `self`
- #
- # ~~~nitish
- # assert "String".strip_nullable_and_params == "String"
- # assert "Array[Int]".strip_nullable_and_params == "Array"
- # assert "Map[Set[String], Set[Int]]".strip_nullable_and_params == "Map"
- # ~~~
- private fun strip_nullable_and_params: String
- do
- var class_name = to_s
-
- var prefix = "nullable "
- if class_name.has_prefix(prefix) then class_name = class_name.substring_from(prefix.length)
-
- var bracket_index = class_name.index_of('[')
- if bracket_index == -1 then return class_name
- return class_name.substring(0, bracket_index)
- end
end
redef class SimpleCollection[E]
private fun class_inheritance_metamodel_json: CString is intern
redef class Sys
- # Class inheritance graph
- #
- # ~~~
- # var hierarchy = class_inheritance_metamodel
- # assert hierarchy.has_edge("String", "Object")
- # assert not hierarchy.has_edge("Object", "String")
- # ~~~
- var class_inheritance_metamodel: POSet[String] is lazy do
+ redef var class_inheritance_metamodel is lazy do
var engine = new JsonDeserializer(class_inheritance_metamodel_json.to_s)
engine.check_subtypes = false
engine.whitelist.add_all(
- ["String", "POSet[String]", "POSetElement[String]", "HashSet[String]", "HashMap[String, POSetElement[String]]"])
+ ["String", "POSet[String]", "POSetElement[String]",
+ "HashSet[String]", "HashMap[String, POSetElement[String]]"])
+
var poset = engine.deserialize
if engine.errors.not_empty then
- print_error engine.errors.join("\n")
+ print_error "Deserialization errors in class_inheritance_metamodel:"
+ print_error engine.errors.join("\n* ")
return new POSet[String]
end
+
if poset isa POSet[String] then return poset
return new POSet[String]
end
end
-
-redef class Deserializer
- redef fun deserialize_class(name)
- do
- if name == "POSet[String]" then return new POSet[String].from_deserializer(self)
- if name == "POSetElement[String]" then return new POSetElement[String].from_deserializer(self)
- if name == "HashSet[String]" then return new HashSet[String].from_deserializer(self)
- if name == "HashMap[String, POSetElement[String]]" then return new HashMap[String, POSetElement[String]].from_deserializer(self)
-
- return super
- end
-end
# be deserialized to their original form using `JsonDeserializer`.
# * Use references when an object has already been serialized so to not duplicate it.
# * Support cycles in references.
- # * Preserve the Nit `Char` type as an object because it does not exist in JSON.
+ # * Preserve the Nit `Char` and `Byte` types as special objects.
# * The generated JSON is standard and can be read by non-Nit programs.
# However, some Nit types are not represented by the simplest possible JSON representation.
# With the added metadata, it can be complex to read.
# which is used by all the serialization engines, not just JSON.
protected fun accept_json_serializer(v: JsonSerializer)
do
- var id = v.cache.new_id_for(self)
v.stream.write "\{"
v.indent_level += 1
if not v.plain_json then
+ var id = v.cache.new_id_for(self)
v.new_line_and_indent
v.stream.write "\"__kind\": \"obj\", \"__id\": "
v.stream.write id.to_s
end
end
+redef class Byte
+ redef fun accept_json_serializer(v)
+ do
+ if v.plain_json then
+ to_i.accept_json_serializer v
+ else
+ v.stream.write "\{\"__kind\": \"byte\", \"__val\": "
+ to_i.accept_json_serializer v
+ v.stream.write "\}"
+ end
+ end
+end
+
redef class CString
redef fun accept_json_serializer(v) do to_s.accept_json_serializer(v)
end
redef class SimpleCollection[E]
redef fun accept_json_serializer(v)
do
- # Register as pseudo object
- if not v.plain_json then
+ if v.plain_json then
+ serialize_to_pure_json v
+ else
+ # Register as pseudo object
var id = v.cache.new_id_for(self)
v.stream.write """{"""
v.indent_level += 1
v.stream.write class_name
v.stream.write """","""
v.new_line_and_indent
+
v.stream.write """"__items": """
serialize_to_pure_json v
+
core_serialize_to v
- else
- serialize_to_pure_json v
- end
- if not v.plain_json then
v.indent_level -= 1
v.new_line_and_indent
v.stream.write "\}"
# This object can then be type checked as usual with `isa` and `as`.
module static
-import error
-private import json_parser
-private import json_lexer
+import parser_base
+intrude import error
redef class Text
#
# assert not "string".json_need_escape
# assert "\\\"string\\\"".json_need_escape
- protected fun json_need_escape: Bool do return has('\\')
+ private fun json_need_escape: Bool do return has('\\')
# Escapes `self` from a JSON string to a Nit string
#
# assert "\\\"string\\\"".json_to_nit_string == "\"string\""
# assert "\\nEscape\\t\\n".json_to_nit_string == "\nEscape\t\n"
# assert "\\u0041zu\\uD800\\uDFD3".json_to_nit_string == "Azu𐏓"
- protected fun json_to_nit_string: String do
+ private fun json_to_nit_string: String do
var res = new FlatBuffer.with_capacity(byte_length)
var i = 0
var ln = self.length
# assert str isa String
# assert str == "foo, bar, baz"
#
- # Example of a syntaxic error:
+ # Example of a syntax error:
#
- # var bad = "\{foo: \"bar\"\}".parse_json
- # assert bad isa JsonParseError
- # assert bad.position.col_start == 2
- fun parse_json: nullable Serializable do
- var lexer = new Lexer_json(to_s)
- var parser = new Parser_json
- var tokens = lexer.lex
- parser.tokens.add_all(tokens)
- var root_node = parser.parse
- if root_node isa NStart then
- return root_node.n_0.to_nit_object
- else if root_node isa NError then
- return new JsonParseError(root_node.message, root_node.position)
- else abort
- end
+ # var error = "\{foo: \"bar\"\}".parse_json
+ # assert error isa JsonParseError
+ # assert error.to_s == "Bad key format Error: bad JSON entity"
+ fun parse_json: nullable Serializable do return (new JSONStringParser(self.to_s)).parse_entity
end
redef class FlatText
end
end
-# A map that can be translated into a JSON object.
-interface JsonMapRead[K: String, V: nullable Serializable]
- super MapRead[K, V]
- super Serializable
-end
-
-# A JSON Object.
-class JsonObject
- super JsonMapRead[String, nullable Serializable]
- super HashMap[String, nullable Serializable]
-end
-
-# A sequence that can be translated into a JSON array.
-class JsonSequenceRead[E: nullable Serializable]
- super Serializable
- super SequenceRead[E]
-end
-
-# A JSON array.
-class JsonArray
- super JsonSequenceRead[nullable Serializable]
- super Array[nullable Serializable]
-end
-
-################################################################################
-# Redef parser
-
-redef class Nvalue
- # The represented value.
- private fun to_nit_object: nullable Serializable is abstract
-end
-
-redef class Nvalue_number
- redef fun to_nit_object
- do
- var text = n_number.text
- if text.chars.has('.') or text.chars.has('e') or text.chars.has('E') then return text.to_f
- return text.to_i
+redef class Char
+ # Is `self` a valid number start ?
+ private fun is_json_num_start: Bool do
+ if self == '-' then return true
+ if self.is_numeric then return true
+ return false
end
-end
-redef class Nvalue_string
- redef fun to_nit_object do return n_string.to_nit_string
-end
-
-redef class Nvalue_true
- redef fun to_nit_object do return true
+ # Is `self` a valid JSON separator ?
+ private fun is_json_separator: Bool do
+ if self == ':' then return true
+ if self == ',' then return true
+ if self == '{' then return true
+ if self == '}' then return true
+ if self == '[' then return true
+ if self == ']' then return true
+ if self == '"' then return true
+ if self.is_whitespace then return true
+ return false
+ end
end
-redef class Nvalue_false
- redef fun to_nit_object do return false
-end
+# A simple ad-hoc JSON parser
+#
+# To parse a simple JSON document, read it as a String and give it to `parse_entity`
+# NOTE: if your document contains several non-nested entities, use `parse_entity` for each
+# JSON entity to parse
+class JSONStringParser
+ super StringProcessor
-redef class Nvalue_null
- redef fun to_nit_object do return null
-end
+ # Parses a JSON Entity
+ #
+ # ~~~nit
+ # var p = new JSONStringParser("""{"numbers": [1,23,3], "string": "string"}""")
+ # assert p.parse_entity isa JsonObject
+ # ~~~
+ fun parse_entity: nullable Serializable do
+ var srclen = len
+ ignore_whitespaces
+ if pos >= srclen then return make_parse_error("Empty JSON")
+ var c = src[pos]
+ if c == '[' then
+ pos += 1
+ return parse_json_array
+ else if c == '"' then
+ var s = parse_json_string
+ return s
+ else if c == '{' then
+ pos += 1
+ return parse_json_object
+ else if c == 'f' then
+ if pos + 4 >= srclen then make_parse_error("Error: bad JSON entity")
+ if src[pos + 1] == 'a' and src[pos + 2] == 'l' and src[pos + 3] == 's' and src[pos + 4] == 'e' then
+ pos += 5
+ return false
+ end
+ return make_parse_error("Error: bad JSON entity")
+ else if c == 't' then
+ if pos + 3 >= srclen then make_parse_error("Error: bad JSON entity")
+ if src[pos + 1] == 'r' and src[pos + 2] == 'u' and src[pos + 3] == 'e' then
+ pos += 4
+ return true
+ end
+ return make_parse_error("Error: bad JSON entity")
+ else if c == 'n' then
+ if pos + 3 >= srclen then make_parse_error("Error: bad JSON entity")
+ if src[pos + 1] == 'u' and src[pos + 2] == 'l' and src[pos + 3] == 'l' then
+ pos += 4
+ return null
+ end
+ return make_parse_error("Error: bad JSON entity")
+ end
+ if not c.is_json_num_start then return make_parse_error("Bad JSON character")
+ return parse_json_number
+ end
-redef class Nstring
- # The represented string.
- private fun to_nit_string: String do return text.substring(1, text.length - 2).unescape_json.to_s
-end
+ # Parses a JSON Array
+ fun parse_json_array: Serializable do
+ var max = len
+ if pos >= max then return make_parse_error("Incomplete JSON array")
+ var arr = new JsonArray
+ var c = src[pos]
+ while not c == ']' do
+ ignore_whitespaces
+ if pos >= max then return make_parse_error("Incomplete JSON array")
+ if src[pos] == ']' then break
+ var ent = parse_entity
+ #print "Parsed an entity {ent} for a JSON array"
+ if ent isa JsonParseError then return ent
+ arr.add ent
+ ignore_whitespaces
+ if pos >= max then return make_parse_error("Incomplete JSON array")
+ c = src[pos]
+ if c == ']' then break
+ if c != ',' then return make_parse_error("Bad array separator {c}")
+ pos += 1
+ end
+ pos += 1
+ return arr
+ end
-redef class Nvalue_object
- redef fun to_nit_object do
+ # Parses a JSON Object
+ fun parse_json_object: Serializable do
+ var max = len
+ if pos >= max then return make_parse_error("Incomplete JSON object")
var obj = new JsonObject
- var members = n_members
- if members != null then
- var pairs = members.pairs
- for pair in pairs do obj[pair.name] = pair.value
+ var c = src[pos]
+ while not c == '}' do
+ ignore_whitespaces
+ if pos >= max then return make_parse_error("Malformed JSON object")
+ if src[pos] == '}' then break
+ var key = parse_entity
+ #print "Parsed key {key} for JSON object"
+ if not key isa String then return make_parse_error("Bad key format {key or else "null"}")
+ ignore_whitespaces
+ if pos >= max then return make_parse_error("Incomplete JSON object")
+ if not src[pos] == ':' then return make_parse_error("Bad key/value separator {src[pos]}")
+ pos += 1
+ ignore_whitespaces
+ var value = parse_entity
+ #print "Parsed value {value} for JSON object"
+ if value isa JsonParseError then return value
+ obj[key] = value
+ ignore_whitespaces
+ if pos >= max then return make_parse_error("Incomplete JSON object")
+ c = src[pos]
+ if c == '}' then break
+ if c != ',' then return make_parse_error("Bad object separator {src[pos]}")
+ pos += 1
end
+ pos += 1
return obj
end
-end
-
-redef class Nmembers
- # All the key-value pairs.
- private fun pairs: Array[Npair] is abstract
-end
-redef class Nmembers_tail
- redef fun pairs
- do
- var arr = n_members.pairs
- arr.add n_pair
- return arr
+ # Creates a `JsonParseError` with the right message and location
+ protected fun make_parse_error(message: String): JsonParseError do
+ var err = new JsonParseError(message)
+ err.location = hot_location
+ return err
end
-end
-redef class Nmembers_head
- redef fun pairs do return [n_pair]
-end
+ # Parses an Int or Float
+ fun parse_json_number: Serializable do
+ var max = len
+ var p = pos
+ var c = src[p]
+ var is_neg = false
+ if c == '-' then
+ is_neg = true
+ p += 1
+ if p >= max then return make_parse_error("Bad JSON number")
+ c = src[p]
+ end
+ var val = 0
+ while c.is_numeric do
+ val *= 10
+ val += c.to_i
+ p += 1
+ if p >= max then break
+ c = src[p]
+ end
+ if c == '.' then
+ p += 1
+ if p >= max then return make_parse_error("Bad JSON number")
+ c = src[p]
+ var fl = val.to_f
+ var frac = 0.1
+ while c.is_numeric do
+ fl += c.to_i.to_f * frac
+ frac /= 10.0
+ p += 1
+ if p >= max then break
+ c = src[p]
+ end
+ if c == 'e' or c == 'E' then
+ p += 1
+ var exp = 0
+ if p >= max then return make_parse_error("Malformed JSON number")
+ c = src[p]
+ while c.is_numeric do
+ exp *= 10
+ exp += c.to_i
+ p += 1
+ if p >= max then break
+ c = src[p]
+ end
+ fl *= (10 ** exp).to_f
+ end
+ if p < max and not c.is_json_separator then return make_parse_error("Malformed JSON number")
+ pos = p
+ if is_neg then return -fl
+ return fl
+ end
+ if c == 'e' or c == 'E' then
+ p += 1
+ if p >= max then return make_parse_error("Bad JSON number")
+ var exp = src[p].to_i
+ c = src[p]
+ while c.is_numeric do
+ exp *= 10
+ exp += c.to_i
+ p += 1
+ if p >= max then break
+ c = src[p]
+ end
+ val *= (10 ** exp)
+ end
+ if p < max and not src[p].is_json_separator then return make_parse_error("Malformed JSON number")
+ pos = p
+ if is_neg then return -val
+ return val
+ end
-redef class Npair
- # The represented key.
- private fun name: String do return n_string.to_nit_string
+ private var parse_str_buf = new FlatBuffer
- # The represented value.
- private fun value: nullable Serializable do return n_value.to_nit_object
-end
+ # Parses and returns a Nit string from a JSON String
+ fun parse_json_string: Serializable do
+ var src = src
+ var ln = src.length
+ var p = pos
+ p += 1
+ if p > ln then return make_parse_error("Malformed JSON String")
+ var c = src[p]
+ var ret = parse_str_buf
+ var chunk_st = p
+ while c != '"' do
+ if c != '\\' then
+ p += 1
+ if p >= ln then return make_parse_error("Malformed JSON string")
+ c = src[p]
+ continue
+ end
+ ret.append_substring_impl(src, chunk_st, p - chunk_st)
+ p += 1
+ if p >= ln then return make_parse_error("Malformed Escape sequence in JSON string")
+ c = src[p]
+ if c == 'r' then
+ ret.add '\r'
+ p += 1
+ else if c == 'n' then
+ ret.add '\n'
+ p += 1
+ else if c == 't' then
+ ret.add '\t'
+ p += 1
+ else if c == 'u' then
+ var cp = 0
+ p += 1
+ for i in [0 .. 4[ do
+ cp <<= 4
+ if p >= ln then make_parse_error("Malformed \uXXXX Escape sequence in JSON string")
+ c = src[p]
+ if c >= '0' and c <= '9' then
+ cp += c.code_point - '0'.code_point
+ else if c >= 'a' and c <= 'f' then
+ cp += c.code_point - 'a'.code_point + 10
+ else if c >= 'A' and c <= 'F' then
+ cp += c.code_point - 'A'.code_point + 10
+ else
+ make_parse_error("Malformed \uXXXX Escape sequence in JSON string")
+ end
+ p += 1
+ end
+ c = cp.code_point
+ if cp >= 0xD800 and cp <= 0xDBFF then
+ if p >= ln then make_parse_error("Malformed \uXXXX Escape sequence in JSON string")
+ c = src[p]
+ if c != '\\' then make_parse_error("Malformed \uXXXX Escape sequence in JSON string")
+ p += 1
+ c = src[p]
+ if c != 'u' then make_parse_error("Malformed \uXXXX Escape sequence in JSON string")
+ var locp = 0
+ p += 1
+ for i in [0 .. 4[ do
+ locp <<= 4
+ if p > ln then make_parse_error("Malformed \uXXXX Escape sequence in JSON string")
+ c = src[p]
+ if c >= '0' and c <= '9' then
+ locp += c.code_point - '0'.code_point
+ else if c >= 'a' and c <= 'f' then
+ locp += c.code_point - 'a'.code_point + 10
+ else if c >= 'A' and c <= 'F' then
+ locp += c.code_point - 'A'.code_point + 10
+ else
+ make_parse_error("Malformed \uXXXX Escape sequence in JSON string")
+ end
+ p += 1
+ end
+ c = (((locp & 0x3FF) | ((cp & 0x3FF) << 10)) + 0x10000).code_point
+ end
+ ret.add c
+ else if c == 'b' then
+ ret.add 8.code_point
+ p += 1
+ else if c == 'f' then
+ ret.add '\f'
+ p += 1
+ else
+ p += 1
+ ret.add c
+ end
+ chunk_st = p
+ c = src[p]
+ end
+ pos = p + 1
+ if ret.is_empty then return src.substring(chunk_st, p - chunk_st)
+ ret.append_substring_impl(src, chunk_st, p - chunk_st)
+ var rets = ret.to_s
+ ret.clear
+ return rets
+ end
-redef class Nvalue_array
- redef fun to_nit_object
- do
- var arr = new JsonArray
- var elements = n_elements
- if elements != null then
- var items = elements.items
- for item in items do arr.add(item.to_nit_object)
+ # Ignores any character until a JSON separator is encountered
+ fun ignore_until_separator do
+ var max = len
+ while pos < max do
+ if not src[pos].is_json_separator then return
end
- return arr
end
end
-redef class Nelements
- # All the items.
- private fun items: Array[Nvalue] is abstract
+# A map that can be translated into a JSON object.
+interface JsonMapRead[K: String, V: nullable Serializable]
+ super MapRead[K, V]
+ super Serializable
end
-redef class Nelements_tail
- redef fun items
- do
- var items = n_elements.items
- items.add(n_value)
- return items
- end
+# A JSON Object.
+class JsonObject
+ super JsonMapRead[String, nullable Serializable]
+ super HashMap[String, nullable Serializable]
end
-redef class Nelements_head
- redef fun items do return [n_value]
+# A sequence that can be translated into a JSON array.
+class JsonSequenceRead[E: nullable Serializable]
+ super Serializable
+ super SequenceRead[E]
+end
+
+# A JSON array.
+class JsonArray
+ super JsonSequenceRead[nullable Serializable]
+ super Array[nullable Serializable]
end
+++ /dev/null
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Simple ad-hoc implementation of a JSON parser for String inputs
-module string_parser
-
-import parser_base
-import static
-
-redef class Char
- # Is `self` a valid number start ?
- private fun is_json_num_start: Bool do
- if self == '-' then return true
- if self.is_numeric then return true
- return false
- end
-
- # Is `self` a valid JSON separator ?
- private fun is_json_separator: Bool do
- if self == ':' then return true
- if self == ',' then return true
- if self == '{' then return true
- if self == '}' then return true
- if self == '[' then return true
- if self == ']' then return true
- if self == '"' then return true
- if self.is_whitespace then return true
- return false
- end
-end
-
-# A simple ad-hoc JSON parser
-#
-# To parse a simple JSON document, read it as a String and give it to `parse_entity`
-# NOTE: if your document contains several non-nested entities, use `parse_entity` for each
-# JSON entity to parse
-class JSONStringParser
- super StringProcessor
-
- # Parses a JSON Entity
- #
- # ~~~nit
- # var p = new JSONStringParser("""{"numbers": [1,23,3], "string": "string"}""")
- # assert p.parse_entity isa JsonObject
- # ~~~
- fun parse_entity: nullable Serializable do
- var srclen = len
- ignore_whitespaces
- if pos >= srclen then return make_parse_error("Empty JSON")
- var c = src[pos]
- if c == '[' then
- pos += 1
- return parse_json_array
- else if c == '"' then
- var s = parse_json_string
- return s
- else if c == '{' then
- pos += 1
- return parse_json_object
- else if c == 'f' then
- if pos + 4 >= srclen then make_parse_error("Error: bad JSON entity")
- if src[pos + 1] == 'a' and src[pos + 2] == 'l' and src[pos + 3] == 's' and src[pos + 4] == 'e' then
- pos += 5
- return false
- end
- return make_parse_error("Error: bad JSON entity")
- else if c == 't' then
- if pos + 3 >= srclen then make_parse_error("Error: bad JSON entity")
- if src[pos + 1] == 'r' and src[pos + 2] == 'u' and src[pos + 3] == 'e' then
- pos += 4
- return true
- end
- return make_parse_error("Error: bad JSON entity")
- else if c == 'n' then
- if pos + 3 >= srclen then make_parse_error("Error: bad JSON entity")
- if src[pos + 1] == 'u' and src[pos + 2] == 'l' and src[pos + 3] == 'l' then
- pos += 4
- return null
- end
- return make_parse_error("Error: bad JSON entity")
- end
- if not c.is_json_num_start then return make_parse_error("Bad JSON character")
- return parse_json_number
- end
-
- # Parses a JSON Array
- fun parse_json_array: Serializable do
- var max = len
- if pos >= max then return make_parse_error("Incomplete JSON array")
- var arr = new JsonArray
- var c = src[pos]
- while not c == ']' do
- ignore_whitespaces
- if pos >= max then return make_parse_error("Incomplete JSON array")
- if src[pos] == ']' then break
- var ent = parse_entity
- #print "Parsed an entity {ent} for a JSON array"
- if ent isa JsonParseError then return ent
- arr.add ent
- ignore_whitespaces
- if pos >= max then return make_parse_error("Incomplete JSON array")
- c = src[pos]
- if c == ']' then break
- if c != ',' then return make_parse_error("Bad array separator {c}")
- pos += 1
- end
- pos += 1
- return arr
- end
-
- # Parses a JSON Object
- fun parse_json_object: Serializable do
- var max = len
- if pos >= max then return make_parse_error("Incomplete JSON object")
- var obj = new JsonObject
- var c = src[pos]
- while not c == '}' do
- ignore_whitespaces
- if pos >= max then return make_parse_error("Malformed JSON object")
- if src[pos] == '}' then break
- var key = parse_entity
- #print "Parsed key {key} for JSON object"
- if not key isa String then return make_parse_error("Bad key format {key or else "null"}")
- ignore_whitespaces
- if pos >= max then return make_parse_error("Incomplete JSON object")
- if not src[pos] == ':' then return make_parse_error("Bad key/value separator {src[pos]}")
- pos += 1
- ignore_whitespaces
- var value = parse_entity
- #print "Parsed value {value} for JSON object"
- if value isa JsonParseError then return value
- obj[key] = value
- ignore_whitespaces
- if pos >= max then return make_parse_error("Incomplete JSON object")
- c = src[pos]
- if c == '}' then break
- if c != ',' then return make_parse_error("Bad object separator {src[pos]}")
- pos += 1
- end
- pos += 1
- return obj
- end
-
- # Creates a `JsonParseError` with the right message and location
- protected fun make_parse_error(message: String): JsonParseError do
- var err = new JsonParseError(message)
- err.location = hot_location
- return err
- end
-
- # Parses an Int or Float
- fun parse_json_number: Serializable do
- var max = len
- var p = pos
- var c = src[p]
- var is_neg = false
- if c == '-' then
- is_neg = true
- p += 1
- if p >= max then return make_parse_error("Bad JSON number")
- c = src[p]
- end
- var val = 0
- while c.is_numeric do
- val *= 10
- val += c.to_i
- p += 1
- if p >= max then break
- c = src[p]
- end
- if c == '.' then
- p += 1
- if p >= max then return make_parse_error("Bad JSON number")
- c = src[p]
- var fl = val.to_f
- var frac = 0.1
- while c.is_numeric do
- fl += c.to_i.to_f * frac
- frac /= 10.0
- p += 1
- if p >= max then break
- c = src[p]
- end
- if c == 'e' or c == 'E' then
- p += 1
- var exp = 0
- if p >= max then return make_parse_error("Malformed JSON number")
- c = src[p]
- while c.is_numeric do
- exp *= 10
- exp += c.to_i
- p += 1
- if p >= max then break
- c = src[p]
- end
- fl *= (10 ** exp).to_f
- end
- if p < max and not c.is_json_separator then return make_parse_error("Malformed JSON number")
- pos = p
- if is_neg then return -fl
- return fl
- end
- if c == 'e' or c == 'E' then
- p += 1
- if p >= max then return make_parse_error("Bad JSON number")
- var exp = src[p].to_i
- c = src[p]
- while c.is_numeric do
- exp *= 10
- exp += c.to_i
- p += 1
- if p >= max then break
- c = src[p]
- end
- val *= (10 ** exp)
- end
- if p < max and not src[p].is_json_separator then return make_parse_error("Malformed JSON number")
- pos = p
- if is_neg then return -val
- return val
- end
-
- private var parse_str_buf = new FlatBuffer
-
- # Parses and returns a Nit string from a JSON String
- fun parse_json_string: Serializable do
- var src = src
- var ln = src.length
- var p = pos
- p += 1
- if p > ln then return make_parse_error("Malformed JSON String")
- var c = src[p]
- var ret = parse_str_buf
- var chunk_st = p
- while c != '"' do
- if c != '\\' then
- p += 1
- if p >= ln then return make_parse_error("Malformed JSON string")
- c = src[p]
- continue
- end
- ret.append_substring_impl(src, chunk_st, p - chunk_st)
- p += 1
- if p >= ln then return make_parse_error("Malformed Escape sequence in JSON string")
- c = src[p]
- if c == 'r' then
- ret.add '\r'
- p += 1
- else if c == 'n' then
- ret.add '\n'
- p += 1
- else if c == 't' then
- ret.add '\t'
- p += 1
- else if c == 'u' then
- var cp = 0
- p += 1
- for i in [0 .. 4[ do
- cp <<= 4
- if p >= ln then make_parse_error("Malformed \uXXXX Escape sequence in JSON string")
- c = src[p]
- if c >= '0' and c <= '9' then
- cp += c.code_point - '0'.code_point
- else if c >= 'a' and c <= 'f' then
- cp += c.code_point - 'a'.code_point + 10
- else if c >= 'A' and c <= 'F' then
- cp += c.code_point - 'A'.code_point + 10
- else
- make_parse_error("Malformed \uXXXX Escape sequence in JSON string")
- end
- p += 1
- end
- c = cp.code_point
- if cp >= 0xD800 and cp <= 0xDBFF then
- if p >= ln then make_parse_error("Malformed \uXXXX Escape sequence in JSON string")
- c = src[p]
- if c != '\\' then make_parse_error("Malformed \uXXXX Escape sequence in JSON string")
- p += 1
- c = src[p]
- if c != 'u' then make_parse_error("Malformed \uXXXX Escape sequence in JSON string")
- var locp = 0
- p += 1
- for i in [0 .. 4[ do
- locp <<= 4
- if p > ln then make_parse_error("Malformed \uXXXX Escape sequence in JSON string")
- c = src[p]
- if c >= '0' and c <= '9' then
- locp += c.code_point - '0'.code_point
- else if c >= 'a' and c <= 'f' then
- locp += c.code_point - 'a'.code_point + 10
- else if c >= 'A' and c <= 'F' then
- locp += c.code_point - 'A'.code_point + 10
- else
- make_parse_error("Malformed \uXXXX Escape sequence in JSON string")
- end
- p += 1
- end
- c = (((locp & 0x3FF) | ((cp & 0x3FF) << 10)) + 0x10000).code_point
- end
- ret.add c
- else if c == 'b' then
- ret.add 8.code_point
- p += 1
- else if c == 'f' then
- ret.add '\f'
- p += 1
- else
- p += 1
- ret.add c
- end
- chunk_st = p
- c = src[p]
- end
- pos = p + 1
- if ret.is_empty then return src.substring(chunk_st, p - chunk_st)
- ret.append_substring_impl(src, chunk_st, p - chunk_st)
- var rets = ret.to_s
- ret.clear
- return rets
- end
-
- # Ignores any character until a JSON separator is encountered
- fun ignore_until_separator do
- var max = len
- while pos < max do
- if not src[pos].is_json_separator then return
- end
- end
-end
-
-redef class Text
- redef fun parse_json do return (new JSONStringParser(self.to_s)).parse_entity
-end
-
-redef class JsonParseError
- serialize
-
- # Location of the error in source
- var location: nullable Location = null
-end
self.native = native
end
- redef fun play
+ redef fun play do play_channel(-1, 0)
+
+ # Play this sound on `channel` (or any channel if -1) and return the channel
+ #
+ # Repeat the sound `loops` times, `loops == 0` plays it once,
+ # `loops == 1` plays it twice and `loops == -1` loops infinitely.
+ fun play_channel(channel, loops: Int): Int
do
var native = native
end
# If there's an error, silently skip
- if error != null then return
+ if error != null then return -1
native = self.native
assert native != null
# Play on any available channel
- mix.play_channel(-1, native, 0)
+ return mix.play_channel(channel, native, loops)
end
end
if not line.is_empty and line.leading < 4 and line.value[line.leading] == '[' then
pos = line.leading + 1
pos = md.read_until(id, pos, ']')
- if not id.is_empty and pos + 2 < line.value.length then
+ if not id.is_empty and pos >= 0 and pos + 2 < line.value.length then
if line.value[pos + 1] == ':' then
pos += 2
pos = md.skip_spaces(pos)
- if line.value[pos] == '<' then
+ if pos >= 0 and line.value[pos] == '<' then
pos += 1
pos = md.read_until(link, pos, '>')
pos += 1
- else
+ else if pos >= 0 then
pos = md.read_until(link, pos, ' ', '\n')
end
if not link.is_empty then
# var db_name = "test_{db_suffix}"
# var db = client.database(db_name)
# db.collection("test").insert(new JsonObject)
- # assert client.database_names.has("test")
+ # assert client.database_names.has(db_name)
# ~~~
fun database_names: Array[String] do
var res = new Array[String]
# Play `chunk` on `channel`
#
# If `channel == -1` the first unreserved channel is used.
- # The sound is repeated `loops` times, `loops == 0` plays it once and
- # `loops == -1` loops infinitely.
- fun play_channel(channel: Int, chunk: MixChunk, loops: Int): Bool `{
- return Mix_PlayChannel(channel, chunk, loops) == 0;
+ # The sound is repeated `loops` times, `loops == 0` plays it once,
+ # `loops == 1` plays it twice and `loops == -1` loops infinitely.
+ #
+ # Returns the channel used, or `-1` on error.
+ fun play_channel(channel: Int, chunk: MixChunk, loops: Int): Int `{
+ return Mix_PlayChannel(channel, chunk, loops);
`}
- # Set the chunk volume out of `mix.max_volume` and return the previous value
+ # Play `chunk` on `channel`
#
- # Use `volume = -1` to only read the previous value.
+ # If `channel == -1` the first unreserved channel is used.
+ # The sound is repeated `loops` times, `loops == 0` plays it once,
+ # `loops == 1` plays it twice and `loops == -1` loops infinitely.
+ # If `ticks != -1`, the sample plays for at most `ticks` milliseconds.
+ fun play_channel_timed(channel: Int, chunk: MixChunk, loops, ticks: Int): Int `{
+ return Mix_PlayChannelTimed(channel, chunk, loops, ticks);
+ `}
+
+ # Halt/stop `channel` playback
+ #
+ # If `channel == -1`, halt all channels.
+ fun halt_channel(channel: Int) `{
+ Mix_HaltChannel(channel);
+ `}
+
+ # Halt `channel` in `ticks` milliseconds and return the number of channels set to expire
+ #
+ # If `channel == -1`, halt all channels.
+ fun expire_channel(channel, ticks: Int): Int `{
+ return Mix_ExpireChannel(channel, ticks);
+ `}
+
+ # Reserve `num` channels from being used by `play_channel(-1...)`
+ #
+ # Returns the number of of channels reserved.
+ fun reserve_channels(num: Int): Int `{
+ return Mix_ReserveChannels(num);
+ `}
+
+ # Set the `volume` of `channel`, out of `mix.max_volume`
+ #
+ # If `channel == -1`, set the volume of all channels.
+ #
+ # Returns the current volume of the channel, or if `channel == -1` the average volume.
+ fun volume(channel, volume: Int): Int `{
+ return Mix_Volume(channel, volume);
+ `}
+
+ # Set the `volume` for `chunk`, out of `mix.max_volume`
+ #
+ # If `volume == -1`, only read the previous value.
+ #
+ # Returns the previous volume value.
fun volume_chunk(chunk: MixChunk, volume: Int) `{
Mix_VolumeChunk(chunk, volume);
`}
+ # Pause `channel`, or all playing channels if -1
+ fun pause(channel: Int) `{
+ Mix_Pause(channel);
+ `}
+
+ # Unpause `channel`, or all paused channels if -1
+ fun resume(channel: Int) `{
+ Mix_Resume(channel);
+ `}
+
# ---
# Music
var c = _array[i]
while c != null do
var ck = c._key
+ assert ck != null
if ck.is_same_serialized(k) then
break
end
return c
end
end
+
+redef interface Object
+ # Is `self` the same as `other` in a serialization context?
+ #
+ # Used to determine if an object has already been serialized.
+ fun is_same_serialized(other: nullable Object): Bool do return is_same_instance(other)
+
+ # Hash value use for serialization
+ #
+ # Used in combination with `is_same_serialized`. If two objects are the same
+ # in a serialization context, they must have the same `serialization_hash`.
+ fun serialization_hash: Int do return object_id
+end
+
+redef class Text
+
+ # Strip the `nullable` prefix from the type name `self`
+ #
+ # ~~~
+ # assert "String".strip_nullable == "String"
+ # assert "nullable Array[Int]".strip_nullable == "Array[Int]"
+ # assert "Map[Set[String], Set[Int]]".strip_nullable == "Map[Set[String], Set[Int]]"
+ # ~~~
+ fun strip_nullable: Text
+ do
+ var prefix = "nullable "
+ return if has_prefix(prefix) then substring_from(prefix.length) else self
+ end
+
+ # Strip the `nullable` prefix and the params from the type name `self`
+ #
+ # ~~~
+ # assert "String".strip_nullable_and_params == "String"
+ # assert "nullable Array[Int]".strip_nullable_and_params == "Array"
+ # assert "Map[Set[String], Set[Int]]".strip_nullable_and_params == "Map"
+ # ~~~
+ fun strip_nullable_and_params: Text
+ do
+ var class_name = strip_nullable
+
+ var bracket_index = class_name.index_of('[')
+ if bracket_index == -1 then return class_name
+ return class_name.substring(0, bracket_index)
+ end
+end
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Services for safer deserialization engines
+module safe
+
+import poset
+
+import serialization
+private import engine_tools
+
+# Deserialization engine limiting which types can be deserialized
+class SafeDeserializer
+ super Deserializer
+
+ # Accepted parameterized classes to deserialize
+ #
+ # If `whitelist.empty`, all types are accepted.
+ #
+ # ~~~
+ # import json
+ #
+ # class MyClass
+ # serialize
+ # end
+ #
+ # var json_string = """
+ # {"__class": "MyClass"}
+ # """
+ #
+ # var deserializer = new JsonDeserializer(json_string)
+ # var obj = deserializer.deserialize
+ # assert deserializer.errors.is_empty
+ # assert obj isa MyClass
+ #
+ # deserializer = new JsonDeserializer(json_string)
+ # deserializer.whitelist.add "Array[String]"
+ # deserializer.whitelist.add "AnotherAcceptedClass"
+ # obj = deserializer.deserialize
+ # assert deserializer.errors.length == 1
+ # assert obj == null
+ # ~~~
+ var whitelist = new Array[Text]
+
+ # Should objects be checked if they a subtype of the static type before deserialization?
+ #
+ # Defaults to `true`, as it should always be activated.
+ # It can be turned off to implement the subtype check itself.
+ var check_subtypes = true is writable
+
+ # Should `self` accept to deserialize an instance of `dynamic_type` for an attribute wuth `static_type`?
+ #
+ # Uses `whitelist` if not empty...
+ # Check correct inheritance if `check_subtypes`...
+ fun accept(dynamic_type: Text, static_type: nullable Text): Bool
+ do
+ if whitelist.not_empty and not whitelist.has(dynamic_type) then
+ errors.add new Error("Deserialization Error: '{dynamic_type}' not in whitelist")
+ return false
+ end
+
+ if static_type != null and check_subtypes then
+ var static_class = static_type.strip_nullable_and_params.to_s
+ var dynamic_class = dynamic_type.strip_nullable_and_params.to_s
+ if not class_inheritance_metamodel.has_edge(dynamic_class, static_class) then
+ errors.add new Error("Deserialization Error: `{dynamic_type}` is not a subtype of the static type `{static_type}`")
+ return false
+ end
+ end
+
+ return true
+ end
+end
+
+redef class Sys
+ # Class inheritance graph, implemented by the `json` package
+ #
+ # ~~~
+ # import json
+ #
+ # var hierarchy = class_inheritance_metamodel
+ # assert hierarchy.has_edge("String", "Object")
+ # assert not hierarchy.has_edge("Object", "String")
+ # ~~~
+ fun class_inheritance_metamodel: POSet[String] is abstract
+end
+
+redef class Deserializer
+ redef fun deserialize_class(name)
+ do
+ if name == "POSet[String]" then return new POSet[String].from_deserializer(self)
+ if name == "POSetElement[String]" then return new POSetElement[String].from_deserializer(self)
+ if name == "HashSet[String]" then return new HashSet[String].from_deserializer(self)
+ if name == "HashMap[String, POSetElement[String]]" then return new HashMap[String, POSetElement[String]].from_deserializer(self)
+
+ return super
+ end
+end
fun serialize_attribute(name: String, value: nullable Object)
do
if not try_to_serialize(value) then
+ assert value != null # null would have been serialized
warn("argument {name} of type {value.class_name} is not serializable.")
end
end
- # Serialize `value` is possie, i.e. it is `Serializable` or `null`
+ # Serialize `value` is possible, i.e. it is `Serializable` or `null`
fun try_to_serialize(value: nullable Object): Bool
do
if value isa Serializable then
# This method should be redefined for each custom subclass of `Serializable`.
# All refinement should look for a precise `class_name` and call super
# on unsupported classes.
- protected fun deserialize_class(class_name: String): nullable Object do
+ protected fun deserialize_class(class_name: Text): nullable Object do
if class_name == "Error" then return new Error.from_deserializer(self)
return deserialize_class_intern(class_name)
end
# Refinements to this method will be generated by the serialization phase.
# To avoid conflicts, there should not be any other refinements to this method.
# You can instead use `deserialize_class`.
- protected fun deserialize_class_intern(class_name: String): nullable Object do
+ protected fun deserialize_class_intern(class_name: Text): nullable Object do
errors.add new Error("Deserialization Error: Doesn't know how to deserialize class \"{class_name}\"")
return null
end
init from_deserializer(deserializer: Deserializer) is nosuper do end
end
-redef interface Object
- # Is `self` the same as `other` in a serialization context?
- #
- # Used to determine if an object has already been serialized.
- fun is_same_serialized(other: nullable Object): Bool do return is_same_instance(other)
-
- # Hash value use for serialization
- #
- # Used in combination with `is_same_serialized`. If two objects are the same
- # in a serialization context, they must have the same `serialization_hash`.
- fun serialization_hash: Int do return object_id
-end
-
# Instances of this class are not delayed and instead serialized immediately
# This applies mainly to `universal` types
interface DirectSerializable
redef class Bool super DirectSerializable end
redef class Char super DirectSerializable end
+redef class Byte super DirectSerializable end
redef class Int super DirectSerializable end
redef class Float super DirectSerializable end
redef class CString super DirectSerializable end
if deserializer_npropdef == null then return
# Collect local types expected to be deserialized
- var types_to_deserialize = new Set[String]
+ #
+ # Map types' `name` to their `full_name`.
+ #
+ # FIXME use only the full name when there's a `class_full_name`
+ var types_to_deserialize = new Map[String, String]
## Local serializable standard class without parameters
for nclassdef in nclassdefs do
if mclass == null then continue
if mclass.arity == 0 and mclass.kind == concrete_kind then
- types_to_deserialize.add mclass.name
+ types_to_deserialize[mclass.name] = mclass.full_name
end
end
break
end
- if is_serializable then types_to_deserialize.add mtype.to_s
+ if is_serializable then types_to_deserialize[mtype.name] = mtype.full_name
end
end
end
code.add "redef fun deserialize_class_intern(name)"
code.add "do"
- for name in types_to_deserialize do
- code.add " if name == \"{name}\" then return new {name}.from_deserializer(self)"
+ for name, full_name in types_to_deserialize do
+
+ if full_name.has('-') then
+ # Invalid module name, it is either artificial or a script
+ # without module declaration (like those generated by nitunit)
+ full_name = name
+ end
+
+ code.add " if name == \"{name}\" then return new {full_name}.from_deserializer(self)"
end
code.add " return super"
alpha_comparator.sort(fs)
# Try each entry as a group or a module
for f in fs do
+ if f.first == '.' then continue
var af = a/f
mgroup = identify_group(af)
if mgroup != null then
var files = p.files
alpha_comparator.sort(files)
for f in files do
+ if f.first == '.' then continue
var fp = p/f
var g = identify_group(fp)
# Recursively scan for groups of the same package
end
end
-redef class Location
+redef class nitc::Location
serialize
redef fun core_serialize_to(v) do
end
end
-redef class Location
+redef class nitc::Location
serialize
# Avoid diff on location absolute path
end
# Get a `Location` from its string representation.
- private fun to_location(loc: String): Location do
- return new Location.from_string(loc)
+ private fun to_location(loc: String): nitc::Location do
+ return new nitc::Location.from_string(loc)
end
# Get a `MVisibility` from its string representation.
Deserialization Error: Doesn't know how to deserialize class "Array"
Deserialization Error: Wrong type on `E::b` expected `Array[nullable Serializable]`, got `null`
# Nit:
-<E: 2222>
+<F: 2222>
# Json:
{"__kind": "obj", "__id": 0, "__class": "F","n":2222}
Deserialization Error: Doesn't know how to deserialize class "F"
# Nit:
-<E: 33.33>
+<F: 33.33>
# Json:
{"__kind": "obj", "__id": 0, "__class": "F","n":33.33}
--- /dev/null
+# Nit:
+<A: true a 0.123 1234 asdf false p4ssw0rd>
+
+# Json:
+{"b":true,"c":"a","f":0.123,"i":1234,"serialization_specific_name":"asdf","n":null}
+
+# Back in Nit:
+<A: true a 0.123 1234 asdf false p4ssw0rd>
+
+# Nit:
+<B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>
+
+# Json:
+{"b":false,"c":"b","f":123.123,"i":2345,"serialization_specific_name":"hjkl","n":12,"ii":1111,"ss":"qwer"}
+
+# Back in Nit:
+<B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>
+
+# Nit:
+<C: <A: true a 0.123 1234 asdf false p4ssw0rd> <B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>>
+
+# Json:
+{"a":{"b":true,"c":"a","f":0.123,"i":1234,"serialization_specific_name":"asdf","n":null},"b":{"b":false,"c":"b","f":123.123,"i":2345,"serialization_specific_name":"hjkl","n":12,"ii":1111,"ss":"qwer"},"aa":{"b":true,"c":"a","f":0.123,"i":1234,"serialization_specific_name":"asdf","n":null}}
+
+# Back in Nit:
+<C: <A: true a 0.123 1234 asdf false p4ssw0rd> <B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>>
+
+Serialization warning: Cycle detected in serialized object, replacing reference with 'null'.
+# Nit:
+<D: <B: <A: false b 123.123 2345 new line ->
+<- false p4ssw0rd> 1111 f"\r\/> true>
+
+# Json:
+{"b":false,"c":"b","f":123.123,"i":2345,"serialization_specific_name":"new line ->\n<-","n":null,"ii":1111,"ss":"\tf\"\r\\/","d":null}
+
+# Back in Nit:
+<D: <B: <A: false b 123.123 2345 new line ->
+<- false p4ssw0rd> 1111 f"\r\/> false>
+
+# Nit:
+<E: a: hello, 1234, 123.4; b: hella, 2345, 234.5>
+
+# Json:
+{"a":["hello",1234,123.4],"b":["hella",2345,234.5]}
+
+# Back in Nit:
+<E: a: hello, 1234, 123.4; b: hella, 2345, 234.5>
+
+# Nit:
+<F: 2222>
+
+# Json:
+{"n":2222}
+
+# Back in Nit:
+null
+
+Deserialization Error: Doesn't know how to deserialize class "F"
+# Nit:
+<F: 33.33>
+
+# Json:
+{"n":33.33}
+
+# Back in Nit:
+null
+
+Deserialization Error: Doesn't know how to deserialize class "F"
+# Nit:
+<G: hs: -1, 0; s: one, two; hm: one. 1, two. 2; am: three. 3, four. 4>
+
+# Json:
+{"hs":[-1,0],"s":["one","two"],"hm":{"one":1,"two":2},"am":{"three":"3","four":"4"}}
+
+# Back in Nit:
+<G: hs: -1, 0; s: ; hm: one. 1, two. 2; am: three. 3, four. 4>
+
+Deserialization Error: Doesn't know how to deserialize class "Set[String]"
+Deserialization Error: Wrong type on `G::s` expected `Set[String]`, got `null`
Deserialization Error: Doesn't know how to deserialize class "Array"
Deserialization Error: Wrong type on `E::b` expected `Array[nullable Serializable]`, got `null`
# Nit:
-<E: 2222>
+<F: 2222>
# Json:
{
Deserialization Error: Doesn't know how to deserialize class "F"
# Nit:
-<E: 33.33>
+<F: 33.33>
# Json:
{
--- /dev/null
+# Nit:
+<A: true a 0.123 1234 asdf false p4ssw0rd>
+
+# Json:
+{
+ "b": true,
+ "c": "a",
+ "f": 0.123,
+ "i": 1234,
+ "serialization_specific_name": "asdf",
+ "n": null
+}
+
+# Back in Nit:
+<A: true a 0.123 1234 asdf false p4ssw0rd>
+
+# Nit:
+<B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>
+
+# Json:
+{
+ "b": false,
+ "c": "b",
+ "f": 123.123,
+ "i": 2345,
+ "serialization_specific_name": "hjkl",
+ "n": 12,
+ "ii": 1111,
+ "ss": "qwer"
+}
+
+# Back in Nit:
+<B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>
+
+# Nit:
+<C: <A: true a 0.123 1234 asdf false p4ssw0rd> <B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>>
+
+# Json:
+{
+ "a": {
+ "b": true,
+ "c": "a",
+ "f": 0.123,
+ "i": 1234,
+ "serialization_specific_name": "asdf",
+ "n": null
+ },
+ "b": {
+ "b": false,
+ "c": "b",
+ "f": 123.123,
+ "i": 2345,
+ "serialization_specific_name": "hjkl",
+ "n": 12,
+ "ii": 1111,
+ "ss": "qwer"
+ },
+ "aa": {
+ "b": true,
+ "c": "a",
+ "f": 0.123,
+ "i": 1234,
+ "serialization_specific_name": "asdf",
+ "n": null
+ }
+}
+
+# Back in Nit:
+<C: <A: true a 0.123 1234 asdf false p4ssw0rd> <B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>>
+
+Serialization warning: Cycle detected in serialized object, replacing reference with 'null'.
+# Nit:
+<D: <B: <A: false b 123.123 2345 new line ->
+<- false p4ssw0rd> 1111 f"\r\/> true>
+
+# Json:
+{
+ "b": false,
+ "c": "b",
+ "f": 123.123,
+ "i": 2345,
+ "serialization_specific_name": "new line ->\n<-",
+ "n": null,
+ "ii": 1111,
+ "ss": "\tf\"\r\\/",
+ "d": null
+}
+
+# Back in Nit:
+<D: <B: <A: false b 123.123 2345 new line ->
+<- false p4ssw0rd> 1111 f"\r\/> false>
+
+# Nit:
+<E: a: hello, 1234, 123.4; b: hella, 2345, 234.5>
+
+# Json:
+{
+ "a": ["hello", 1234, 123.4],
+ "b": ["hella", 2345, 234.5]
+}
+
+# Back in Nit:
+<E: a: hello, 1234, 123.4; b: hella, 2345, 234.5>
+
+# Nit:
+<F: 2222>
+
+# Json:
+{
+ "n": 2222
+}
+
+# Back in Nit:
+null
+
+Deserialization Error: Doesn't know how to deserialize class "F"
+# Nit:
+<F: 33.33>
+
+# Json:
+{
+ "n": 33.33
+}
+
+# Back in Nit:
+null
+
+Deserialization Error: Doesn't know how to deserialize class "F"
+# Nit:
+<G: hs: -1, 0; s: one, two; hm: one. 1, two. 2; am: three. 3, four. 4>
+
+# Json:
+{
+ "hs": [-1, 0],
+ "s": ["one", "two"],
+ "hm": {
+ "one": 1,
+ "two": 2
+ },
+ "am": {
+ "three": "3",
+ "four": "4"
+ }
+}
+
+# Back in Nit:
+<G: hs: -1, 0; s: ; hm: one. 1, two. 2; am: three. 3, four. 4>
+
+Deserialization Error: Doesn't know how to deserialize class "Set[String]"
+Deserialization Error: Wrong type on `G::s` expected `Set[String]`, got `null`
--- /dev/null
+# Nit:
+<A: true a 0.123 1234 asdf false p4ssw0rd>
+
+# Json:
+{"b":true,"c":"a","f":0.123,"i":1234,"serialization_specific_name":"asdf","n":null}
+
+# Back in Nit:
+<A: true a 0.123 1234 asdf false p4ssw0rd>
+
+# Nit:
+<B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>
+
+# Json:
+{"b":false,"c":"b","f":123.123,"i":2345,"serialization_specific_name":"hjkl","n":12,"ii":1111,"ss":"qwer"}
+
+# Back in Nit:
+<B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>
+
+# Nit:
+<C: <A: true a 0.123 1234 asdf false p4ssw0rd> <B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>>
+
+# Json:
+{"a":{"b":true,"c":"a","f":0.123,"i":1234,"serialization_specific_name":"asdf","n":null},"b":{"b":false,"c":"b","f":123.123,"i":2345,"serialization_specific_name":"hjkl","n":12,"ii":1111,"ss":"qwer"},"aa":{"b":true,"c":"a","f":0.123,"i":1234,"serialization_specific_name":"asdf","n":null}}
+
+# Back in Nit:
+<C: <A: true a 0.123 1234 asdf false p4ssw0rd> <B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>>
+
+Serialization warning: Cycle detected in serialized object, replacing reference with 'null'.
+# Nit:
+<D: <B: <A: false b 123.123 2345 new line ->
+<- false p4ssw0rd> 1111 f"\r\/> true>
+
+# Json:
+{"b":false,"c":"b","f":123.123,"i":2345,"serialization_specific_name":"new line ->\n<-","n":null,"ii":1111,"ss":"\tf\"\r\\/","d":null}
+
+# Back in Nit:
+<D: <B: <A: false b 123.123 2345 new line ->
+<- false p4ssw0rd> 1111 f"\r\/> false>
+
+# Nit:
+<E: a: hello, 1234, 123.4; b: hella, 2345, 234.5>
+
+# Json:
+{"a":["hello",1234,123.4],"b":["hella",2345,234.5]}
+
+# Back in Nit:
+<E: a: hello, 1234, 123.4; b: hella, 2345, 234.5>
+
+# Nit:
+<F: 2222>
+
+# Json:
+{"n":2222}
+
+# Back in Nit:
+<F: 2222>
+
+# Nit:
+<F: 33.33>
+
+# Json:
+{"n":33.33}
+
+# Back in Nit:
+<F: 33.33>
+
+# Nit:
+<G: hs: -1, 0; s: one, two; hm: one. 1, two. 2; am: three. 3, four. 4>
+
+# Json:
+{"hs":[-1,0],"s":["one","two"],"hm":{"one":1,"two":2},"am":{"three":"3","four":"4"}}
+
+# Back in Nit:
+<G: hs: -1, 0; s: ; hm: ; am: >
+
+Deserialization Error: Doesn't know how to deserialize class "Set[String]"
+Deserialization Error: Wrong type on `G::s` expected `Set[String]`, got `null`
+Deserialization Error: Wrong type on `G::hm` expected `HashMap[String, Int]`, got `Bool`
+Deserialization Error: Wrong type on `G::am` expected `ArrayMap[String, String]`, got `Bool`
--- /dev/null
+# Nit:
+<A: true a 0.123 1234 asdf false p4ssw0rd>
+
+# Json:
+{
+ "b": true,
+ "c": "a",
+ "f": 0.123,
+ "i": 1234,
+ "serialization_specific_name": "asdf",
+ "n": null
+}
+
+# Back in Nit:
+<A: true a 0.123 1234 asdf false p4ssw0rd>
+
+# Nit:
+<B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>
+
+# Json:
+{
+ "b": false,
+ "c": "b",
+ "f": 123.123,
+ "i": 2345,
+ "serialization_specific_name": "hjkl",
+ "n": 12,
+ "ii": 1111,
+ "ss": "qwer"
+}
+
+# Back in Nit:
+<B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>
+
+# Nit:
+<C: <A: true a 0.123 1234 asdf false p4ssw0rd> <B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>>
+
+# Json:
+{
+ "a": {
+ "b": true,
+ "c": "a",
+ "f": 0.123,
+ "i": 1234,
+ "serialization_specific_name": "asdf",
+ "n": null
+ },
+ "b": {
+ "b": false,
+ "c": "b",
+ "f": 123.123,
+ "i": 2345,
+ "serialization_specific_name": "hjkl",
+ "n": 12,
+ "ii": 1111,
+ "ss": "qwer"
+ },
+ "aa": {
+ "b": true,
+ "c": "a",
+ "f": 0.123,
+ "i": 1234,
+ "serialization_specific_name": "asdf",
+ "n": null
+ }
+}
+
+# Back in Nit:
+<C: <A: true a 0.123 1234 asdf false p4ssw0rd> <B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>>
+
+Serialization warning: Cycle detected in serialized object, replacing reference with 'null'.
+# Nit:
+<D: <B: <A: false b 123.123 2345 new line ->
+<- false p4ssw0rd> 1111 f"\r\/> true>
+
+# Json:
+{
+ "b": false,
+ "c": "b",
+ "f": 123.123,
+ "i": 2345,
+ "serialization_specific_name": "new line ->\n<-",
+ "n": null,
+ "ii": 1111,
+ "ss": "\tf\"\r\\/",
+ "d": null
+}
+
+# Back in Nit:
+<D: <B: <A: false b 123.123 2345 new line ->
+<- false p4ssw0rd> 1111 f"\r\/> false>
+
+# Nit:
+<E: a: hello, 1234, 123.4; b: hella, 2345, 234.5>
+
+# Json:
+{
+ "a": ["hello", 1234, 123.4],
+ "b": ["hella", 2345, 234.5]
+}
+
+# Back in Nit:
+<E: a: hello, 1234, 123.4; b: hella, 2345, 234.5>
+
+# Nit:
+<F: 2222>
+
+# Json:
+{
+ "n": 2222
+}
+
+# Back in Nit:
+<F: 2222>
+
+# Nit:
+<F: 33.33>
+
+# Json:
+{
+ "n": 33.33
+}
+
+# Back in Nit:
+<F: 33.33>
+
+# Nit:
+<G: hs: -1, 0; s: one, two; hm: one. 1, two. 2; am: three. 3, four. 4>
+
+# Json:
+{
+ "hs": [-1, 0],
+ "s": ["one", "two"],
+ "hm": {
+ "one": 1,
+ "two": 2
+ },
+ "am": {
+ "three": "3",
+ "four": "4"
+ }
+}
+
+# Back in Nit:
+<G: hs: -1, 0; s: ; hm: ; am: >
+
+Deserialization Error: Doesn't know how to deserialize class "Set[String]"
+Deserialization Error: Wrong type on `G::s` expected `Set[String]`, got `null`
+Deserialization Error: Wrong type on `G::hm` expected `HashMap[String, Int]`, got `Bool`
+Deserialization Error: Wrong type on `G::am` expected `ArrayMap[String, String]`, got `Bool`
redef fun deserialize_class(name)
do
# Module: test_serialization
- if name == "Array[Text]" then return new Array[Text].from_deserializer(self)
if name == "Array[Map[String, nullable Object]]" then return new Array[Map[String, nullable Object]].from_deserializer(self)
if name == "Array[String]" then return new Array[String].from_deserializer(self)
- if name == "StrictHashMap[Int, Object]" then return new StrictHashMap[Int, Object].from_deserializer(self)
+ if name == "Array[Text]" then return new Array[Text].from_deserializer(self)
if name == "Array[Error]" then return new Array[Error].from_deserializer(self)
+ if name == "StrictHashMap[Int, Object]" then return new StrictHashMap[Int, Object].from_deserializer(self)
if name == "POSet[String]" then return new POSet[String].from_deserializer(self)
if name == "Array[Int]" then return new Array[Int].from_deserializer(self)
if name == "Array[nullable Object]" then return new Array[nullable Object].from_deserializer(self)
process http://localhost:3000/api/entity/core. 1+0/1
Error with http://localhost:3000/api/entity/core
-http://localhost:3000/api/entity/core: Unexpected Eof; is acceptable instead: value
+http://localhost:3000/api/entity/core: Empty JSON
<E: a: hello, 1234, 123.4; b: hella, 2345, 234.5>
# Src:
-<E: 2222>
+<F: 2222>
# Dst:
-<E: 2222>
+<F: 2222>
# Src:
-<E: 33.33>
+<F: 33.33>
# Dst:
-<E: 33.33>
+<F: 33.33>
# Src:
<G: hs: -1, 0; s: one, two; hm: one. 1, two. 2; am: three. 3, four. 4>
<E: a: hello, 1234, 123.4; b: hella, 2345, 234.5>
# Nit:
-<E: 2222>
+<F: 2222>
# Json:
{"__kind": "obj", "__id": 0, "__class": "F[Int]","n":2222}
# Back in Nit:
-<E: 2222>
+<F: 2222>
# Nit:
-<E: 33.33>
+<F: 33.33>
# Json:
{"__kind": "obj", "__id": 0, "__class": "F[Float]","n":33.33}
# Back in Nit:
-<E: 33.33>
+<F: 33.33>
# Nit:
<G: hs: -1, 0; s: one, two; hm: one. 1, two. 2; am: three. 3, four. 4>
# Json:
{"b":true,"c":"a","f":0.123,"i":1234,"serialization_specific_name":"asdf","n":null}
+# Back in Nit:
+<A: true a 0.123 1234 asdf false p4ssw0rd>
+
# Nit:
<B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>
# Json:
{"b":false,"c":"b","f":123.123,"i":2345,"serialization_specific_name":"hjkl","n":12,"ii":1111,"ss":"qwer"}
+# Back in Nit:
+<B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>
+
# Nit:
<C: <A: true a 0.123 1234 asdf false p4ssw0rd> <B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>>
# Json:
{"a":{"b":true,"c":"a","f":0.123,"i":1234,"serialization_specific_name":"asdf","n":null},"b":{"b":false,"c":"b","f":123.123,"i":2345,"serialization_specific_name":"hjkl","n":12,"ii":1111,"ss":"qwer"},"aa":{"b":true,"c":"a","f":0.123,"i":1234,"serialization_specific_name":"asdf","n":null}}
+# Back in Nit:
+<C: <A: true a 0.123 1234 asdf false p4ssw0rd> <B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>>
+
Serialization warning: Cycle detected in serialized object, replacing reference with 'null'.
# Nit:
<D: <B: <A: false b 123.123 2345 new line ->
# Json:
{"b":false,"c":"b","f":123.123,"i":2345,"serialization_specific_name":"new line ->\n<-","n":null,"ii":1111,"ss":"\tf\"\r\\/","d":null}
+# Back in Nit:
+<D: <B: <A: false b 123.123 2345 new line ->
+<- false p4ssw0rd> 1111 f"\r\/> false>
+
# Nit:
<E: a: hello, 1234, 123.4; b: hella, 2345, 234.5>
# Json:
{"a":["hello",1234,123.4],"b":["hella",2345,234.5]}
+# Back in Nit:
+<E: a: hello, 1234, 123.4; b: hella, 2345, 234.5>
+
# Nit:
-<E: 2222>
+<F: 2222>
# Json:
{"n":2222}
+# Back in Nit:
+<F: 2222>
+
# Nit:
-<E: 33.33>
+<F: 33.33>
# Json:
{"n":33.33}
+# Back in Nit:
+<F: 33.33>
+
# Nit:
<G: hs: -1, 0; s: one, two; hm: one. 1, two. 2; am: three. 3, four. 4>
# Json:
{"hs":[-1,0],"s":["one","two"],"hm":{"one":1,"two":2},"am":{"three":"3","four":"4"}}
+# Back in Nit:
+<G: hs: -1, 0; s: ; hm: one. 1, two. 2; am: three. 3, four. 4>
+
+Deserialization Error: Doesn't know how to deserialize class "Set[String]"
+Deserialization Error: Wrong type on `G::s` expected `Set[String]`, got `null`
<E: a: hello, 1234, 123.4; b: hella, 2345, 234.5>
# Nit:
-<E: 2222>
+<F: 2222>
# Json:
{
}
# Back in Nit:
-<E: 2222>
+<F: 2222>
# Nit:
-<E: 33.33>
+<F: 33.33>
# Json:
{
}
# Back in Nit:
-<E: 33.33>
+<F: 33.33>
# Nit:
<G: hs: -1, 0; s: one, two; hm: one. 1, two. 2; am: three. 3, four. 4>
"n": null
}
+# Back in Nit:
+<A: true a 0.123 1234 asdf false p4ssw0rd>
+
# Nit:
<B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>
"ss": "qwer"
}
+# Back in Nit:
+<B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>
+
# Nit:
<C: <A: true a 0.123 1234 asdf false p4ssw0rd> <B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>>
}
}
+# Back in Nit:
+<C: <A: true a 0.123 1234 asdf false p4ssw0rd> <B: <A: false b 123.123 2345 hjkl true p4ssw0rd> 1111 qwer>>
+
Serialization warning: Cycle detected in serialized object, replacing reference with 'null'.
# Nit:
<D: <B: <A: false b 123.123 2345 new line ->
"d": null
}
+# Back in Nit:
+<D: <B: <A: false b 123.123 2345 new line ->
+<- false p4ssw0rd> 1111 f"\r\/> false>
+
# Nit:
<E: a: hello, 1234, 123.4; b: hella, 2345, 234.5>
"b": ["hella", 2345, 234.5]
}
+# Back in Nit:
+<E: a: hello, 1234, 123.4; b: hella, 2345, 234.5>
+
# Nit:
-<E: 2222>
+<F: 2222>
# Json:
{
"n": 2222
}
+# Back in Nit:
+<F: 2222>
+
# Nit:
-<E: 33.33>
+<F: 33.33>
# Json:
{
"n": 33.33
}
+# Back in Nit:
+<F: 33.33>
+
# Nit:
<G: hs: -1, 0; s: one, two; hm: one. 1, two. 2; am: three. 3, four. 4>
}
}
+# Back in Nit:
+<G: hs: -1, 0; s: ; hm: one. 1, two. 2; am: three. 3, four. 4>
+
+Deserialization Error: Doesn't know how to deserialize class "Set[String]"
+Deserialization Error: Wrong type on `G::s` expected `Set[String]`, got `null`
# See the License for the specific language governing permissions and
# limitations under the License.
-import json::string_parser
+import json::static
import json
if args.length < 1 then
var n: N
- redef fun to_s do return "<E: {n}>"
+ redef fun to_s do return "<F: {n}>"
end
# Other collections
# See the License for the specific language governing permissions and
# limitations under the License.
-import test_deserialization
import json
-#alt1# import test_deserialization_serial
-#alt3# import test_deserialization_serial
+
+import test_deserialization
+import test_deserialization_serial
var entities = new TestEntities
#alt4#serializer.pretty_json = true
serializer.serialize(o)
- var deserializer = new JsonDeserializer(stream.to_s)#alt2##alt4#
- var deserialized = deserializer.deserialize#alt2##alt4#
+ var type_name: nullable String = o.class_name
+ type_name = null #alt2##alt4#
+ var deserializer = new JsonDeserializer(stream.to_s)
+ var deserialized = deserializer.deserialize(type_name)
print "# Nit:\n{o}\n"
print "# Json:\n{stream}\n"
- print "# Back in Nit:\n{deserialized or else "null"}\n"#alt2##alt4#
- if deserializer.errors.not_empty then print deserializer.errors.join("\n")#alt2##alt4#
+ print "# Back in Nit:\n{deserialized or else "null"}\n"
+ if deserializer.errors.not_empty then print deserializer.errors.join("\n")
end