TODO: `default_value` issues.
parent
adda53433b
commit
6b8b45bf6b
|
@ -4,8 +4,8 @@ module Api
|
|||
|
||||
def index
|
||||
render json: sequences
|
||||
.to_a
|
||||
.map { |s| CeleryScript::FetchCelery.run!(sequence: s) }
|
||||
.to_a
|
||||
.map { |s| CeleryScript::FetchCelery.run!(sequence: s) }
|
||||
end
|
||||
|
||||
def show
|
||||
|
@ -18,8 +18,8 @@ module Api
|
|||
|
||||
def update
|
||||
mutate Sequences::Update.run(sequence_params,
|
||||
device: current_device,
|
||||
sequence: sequence)
|
||||
device: current_device,
|
||||
sequence: sequence)
|
||||
end
|
||||
|
||||
def destroy
|
||||
|
@ -29,7 +29,7 @@ module Api
|
|||
private
|
||||
|
||||
def sequence_params
|
||||
@sequence_params ||= raw_json[:sequence] || raw_json || {}
|
||||
@sequence_params ||= raw_json[:sequence] || raw_json || {}
|
||||
end
|
||||
|
||||
def sequences
|
||||
|
|
|
@ -6,9 +6,10 @@
|
|||
module CeleryScript
|
||||
class AstLeaf < AstBase
|
||||
FRIENDLY_ERRORS = CeleryScript::Checker::FRIENDLY_ERRORS
|
||||
BAD_LEAF = CeleryScript::Checker::BAD_LEAF
|
||||
BAD_LEAF = CeleryScript::Checker::BAD_LEAF
|
||||
|
||||
attr_reader :kind, :value, :parent
|
||||
|
||||
def initialize(parent, value, kind)
|
||||
@parent, @value, @kind = parent, value, kind
|
||||
end
|
||||
|
@ -17,14 +18,13 @@ module CeleryScript
|
|||
allowed = corpus.fetchArg(kind).allowed_values
|
||||
unless allowed.any? { |spec| spec.valid?(self, corpus) }
|
||||
message = (FRIENDLY_ERRORS.dig(kind, parent.kind) || BAD_LEAF) % {
|
||||
kind: kind,
|
||||
kind: kind,
|
||||
parent_kind: parent.kind,
|
||||
allowed: "[#{allowed.map(&:name).join(", ")}]",
|
||||
actual: value.class
|
||||
allowed: "[#{allowed.map(&:name).join(", ")}]",
|
||||
actual: value.class,
|
||||
}
|
||||
raise TypeCheckError, message
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,102 +3,102 @@
|
|||
# other nodes (*always* optional).
|
||||
module CeleryScript
|
||||
class AstNode < AstBase
|
||||
attr_reader :args, :body, :comment, :kind, :parent
|
||||
BODY_HAS_NON_NODES = "The `body` of a node can only contain nodes- " \
|
||||
"no leaves here."
|
||||
LEAVES_NEED_KEYS = "Tried to initialize a leaf without a key."
|
||||
NEVER = :__NEVER__
|
||||
FRIENDLY_ERRORS = CeleryScript::Checker::FRIENDLY_ERRORS
|
||||
BAD_LEAF = CeleryScript::Checker::BAD_LEAF
|
||||
attr_reader :args, :body, :comment, :kind, :parent
|
||||
BODY_HAS_NON_NODES = "The `body` of a node can only contain nodes- " \
|
||||
"no leaves here."
|
||||
LEAVES_NEED_KEYS = "Tried to initialize a leaf without a key."
|
||||
NEVER = :__NEVER__
|
||||
FRIENDLY_ERRORS = CeleryScript::Checker::FRIENDLY_ERRORS
|
||||
BAD_LEAF = CeleryScript::Checker::BAD_LEAF
|
||||
|
||||
def initialize(parent = nil, args:, body: nil, comment: "", kind:, uuid: nil)
|
||||
@comment, @kind, @parent = comment, kind, parent
|
||||
def initialize(parent = nil, args:, body: nil, comment: "", kind:, uuid: nil)
|
||||
@comment, @kind, @parent = comment, kind, parent
|
||||
|
||||
@args = args.map do |key, value|
|
||||
[key.to_sym, maybe_initialize(self, value, key)]
|
||||
end.to_h if args
|
||||
@args = args.map do |key, value|
|
||||
[key.to_sym, maybe_initialize(self, value, key)]
|
||||
end.to_h if args
|
||||
|
||||
@body = body.map do |e|
|
||||
raise TypeCheckError, BODY_HAS_NON_NODES unless is_node?(e)
|
||||
maybe_initialize(self, e)
|
||||
end if body
|
||||
@body = body.map do |e|
|
||||
raise TypeCheckError, BODY_HAS_NON_NODES unless is_node?(e)
|
||||
maybe_initialize(self, e)
|
||||
end if body
|
||||
end
|
||||
|
||||
def maybe_initialize(parent, leaf_or_node, key = NEVER)
|
||||
if is_node?(leaf_or_node)
|
||||
AstNode.new(parent, leaf_or_node)
|
||||
else
|
||||
raise TypeCheckError, LEAVES_NEED_KEYS if key == NEVER
|
||||
AstLeaf.new(parent, leaf_or_node, key)
|
||||
end
|
||||
end
|
||||
|
||||
def is_node?(hash)
|
||||
hash.is_a?(Hash) &&
|
||||
hash.symbolize_keys! &&
|
||||
hash.has_key?(:kind) &&
|
||||
hash.has_key?(:args) &&
|
||||
(hash[:body].is_a?(Array) || hash[:body] == nil) &&
|
||||
(hash[:comment].is_a?(String) || hash[:comment] == nil) &&
|
||||
(hash[:args].is_a?(Hash)) &&
|
||||
(hash[:kind].is_a?(String))
|
||||
end
|
||||
|
||||
# Calling this method with only one parameter
|
||||
# indicates a starting condition 🏁
|
||||
def resolve_variable!(origin = self)
|
||||
locals = args[:locals]
|
||||
|
||||
if locals&.kind === "scope_declaration"
|
||||
label = origin.args[:label]&.value
|
||||
result = (locals.body || []).select do |x|
|
||||
x.args[:label]&.value == label
|
||||
end.first
|
||||
return result if result
|
||||
end
|
||||
|
||||
def maybe_initialize(parent, leaf_or_node, key = NEVER)
|
||||
if is_node?(leaf_or_node)
|
||||
AstNode.new(parent, leaf_or_node)
|
||||
else
|
||||
raise TypeCheckError, LEAVES_NEED_KEYS if key == NEVER
|
||||
AstLeaf.new(parent, leaf_or_node, key)
|
||||
end
|
||||
case parent
|
||||
when AstNode
|
||||
# sequence: Check the `scope` arg
|
||||
# Keep recursing if we can't find a scope on this node
|
||||
parent.resolve_variable!(origin)
|
||||
when nil # We've got an unbound variable.
|
||||
origin.invalidate!(UNBOUND_VAR % origin.args[:label].value)
|
||||
end
|
||||
end
|
||||
|
||||
def is_node?(hash)
|
||||
hash.is_a?(Hash) &&
|
||||
hash.symbolize_keys! &&
|
||||
hash.has_key?(:kind) &&
|
||||
hash.has_key?(:args) &&
|
||||
(hash[:body].is_a?(Array) || hash[:body] == nil) &&
|
||||
(hash[:comment].is_a?(String) || hash[:comment] == nil) &&
|
||||
(hash[:args].is_a?(Hash)) &&
|
||||
(hash[:kind].is_a?(String))
|
||||
end
|
||||
|
||||
# Calling this method with only one parameter
|
||||
# indicates a starting condition 🏁
|
||||
def resolve_variable!(origin = self)
|
||||
locals = args[:locals]
|
||||
|
||||
if locals&.kind === "scope_declaration"
|
||||
label = origin.args[:label]&.value
|
||||
result = (locals.body || []).select do |x|
|
||||
x.args[:label]&.value == label
|
||||
end.first
|
||||
return result if result
|
||||
end
|
||||
|
||||
case parent
|
||||
when AstNode
|
||||
# sequence: Check the `scope` arg
|
||||
# Keep recursing if we can't find a scope on this node
|
||||
parent.resolve_variable!(origin)
|
||||
when nil # We've got an unbound variable.
|
||||
origin.invalidate!(UNBOUND_VAR % origin.args[:label].value)
|
||||
end
|
||||
end
|
||||
|
||||
def todo
|
||||
def todo
|
||||
|
||||
# Don't delete this- it is currently unreachable code, but as soon as we
|
||||
# allow identifiers other than `point`, `tool` and `coordinate` we will
|
||||
# need it again (and can write tests)
|
||||
end
|
||||
end
|
||||
|
||||
def cross_check(corpus, key)
|
||||
actual = kind
|
||||
allowed = corpus.fetchArg(key).allowed_values
|
||||
# It would be safe to run type checking here.
|
||||
if (actual == "identifier")
|
||||
allowed_types = allowed.filter { |x| x.tag == :identifier }
|
||||
var = resolve_variable!
|
||||
case var.kind
|
||||
when "parameter_declaration" then todo
|
||||
when "variable_declaration"
|
||||
# REASSIGNMENT WARNING!:
|
||||
actual = var.args[:data_value].kind
|
||||
end
|
||||
end
|
||||
|
||||
unless allowed.map(&:name).include?(actual)
|
||||
message = (FRIENDLY_ERRORS.dig(kind, parent.kind) || BAD_LEAF) % {
|
||||
kind: kind,
|
||||
parent_kind: parent.kind,
|
||||
allowed: allowed.map(&:name),
|
||||
actual: actual
|
||||
}
|
||||
|
||||
raise TypeCheckError, message
|
||||
def cross_check(corpus, key)
|
||||
actual = kind
|
||||
allowed = corpus.fetchArg(key).allowed_values
|
||||
# It would be safe to run type checking here.
|
||||
if (actual == "identifier")
|
||||
allowed_types = allowed.filter { |x| x.tag == :identifier }
|
||||
var = resolve_variable!
|
||||
case var.kind
|
||||
when "parameter_declaration" then todo
|
||||
when "variable_declaration"
|
||||
# REASSIGNMENT WARNING!:
|
||||
actual = var.args[:data_value].kind
|
||||
end
|
||||
end
|
||||
|
||||
unless allowed.map(&:name).include?(actual)
|
||||
message = (FRIENDLY_ERRORS.dig(kind, parent.kind) || BAD_LEAF) % {
|
||||
kind: kind,
|
||||
parent_kind: parent.kind,
|
||||
allowed: allowed.map(&:name),
|
||||
actual: actual,
|
||||
}
|
||||
|
||||
raise TypeCheckError, message
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -28,9 +28,8 @@ module CeleryScript
|
|||
variable_declaration: MISSING_VAR,
|
||||
parameter_declaration: MISSING_VAR,
|
||||
read_pin: "You must select a Sensor in the Read Sensor step.",
|
||||
move_to: "You must select a Location in the Move To step.",
|
||||
execute: "You must select a Sequence in the Execute step.",
|
||||
# default_value: "TODO",
|
||||
move_absolute: "You must select a Location in the Move To step.",
|
||||
execute: CeleryScriptSettingsBag::NO_SUB_SEQ,
|
||||
},
|
||||
}.with_indifferent_access
|
||||
|
||||
|
|
|
@ -67,7 +67,7 @@ module CeleryScriptSettingsBag
|
|||
BAD_TOOL_ID = "Tool #%s does not exist."
|
||||
CANT_ANALOG = "Analog modes are not supported for Box LEDs"
|
||||
NO_PIN_ID = "%s requires a valid pin number"
|
||||
NO_SUB_SEQ = "missing a sequence selection for `execute` block."
|
||||
NO_SUB_SEQ = "You must select a Sequence in the Execute step."
|
||||
ONLY_ONE_COORD = "Move Absolute does not accept a group of locations " \
|
||||
"as input. Please change your selection to a single" \
|
||||
" location."
|
||||
|
|
|
@ -82,7 +82,7 @@ describe CeleryScript::Checker do
|
|||
]
|
||||
chk = CeleryScript::Checker.new(tree, corpus, device)
|
||||
expect(chk.valid?).to be false
|
||||
expect(chk.error.message).to eq("missing a sequence selection for `execute` block.")
|
||||
expect(chk.error.message).to eq("You must select a Sequence in the Execute step.")
|
||||
end
|
||||
|
||||
it "validates peripheral presence" do
|
||||
|
|
Loading…
Reference in New Issue