Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: crystal-lang/crystal
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: bf6b743aa764
Choose a base ref
...
head repository: crystal-lang/crystal
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: afdd36617ae0
Choose a head ref
  • 4 commits
  • 12 files changed
  • 1 contributor

Commits on Sep 27, 2016

  1. Parser: added missing locations to some nodes

    Ary Borenszweig committed Sep 27, 2016

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    0a9490b View commit details
  2. Fixed #3348: IO#cooked and IO#cooked! don't compile

    Ary Borenszweig committed Sep 27, 2016
    Copy the full SHA
    75d958a View commit details
  3. YAML: use libyaml emitter. Fixes #3353

    Ary Borenszweig committed Sep 27, 2016
    2
    Copy the full SHA
    20aa5d3 View commit details
  4. YAML: free memory as soon as possible

    Ary Borenszweig committed Sep 27, 2016
    Copy the full SHA
    afdd366 View commit details
3 changes: 3 additions & 0 deletions spec/std/io/io_spec.cr
Original file line number Diff line number Diff line change
@@ -713,4 +713,7 @@ describe IO do
end
end
end

typeof(STDIN.cooked { })
typeof(STDIN.cooked!)
end
8 changes: 6 additions & 2 deletions spec/std/yaml/serialization_spec.cr
Original file line number Diff line number Diff line change
@@ -132,6 +132,10 @@ describe "YAML serialization" do
String.from_yaml("hello".to_yaml).should eq("hello")
end

it "does for String withs stars (#3353)" do
String.from_yaml("***".to_yaml).should eq("***")
end

it "does for String with quote" do
String.from_yaml("hel\"lo".to_yaml).should eq("hel\"lo")
end
@@ -191,7 +195,7 @@ describe "YAML serialization" do
:null => nil,
}

expected = "--- \nhello: World\ninteger: 2\nfloat: 3.5\nhash: \n a: 1\n b: 2\narray: \n - 1\n - 2\n - 3\nnull: "
expected = "---\nhello: World\ninteger: 2\nfloat: 3.5\nhash:\n a: 1\n b: 2\narray:\n- 1\n- 2\n- 3\nnull: \n"

data.to_yaml.should eq(expected)
end
@@ -200,7 +204,7 @@ describe "YAML serialization" do
string = String.build do |str|
%w(a b c).to_yaml(str)
end
string.should eq("--- \n- a\n- b\n- c")
string.should eq("---\n- a\n- b\n- c\n")
end
end
end
4 changes: 2 additions & 2 deletions spec/std/yaml/yaml_spec.cr
Original file line number Diff line number Diff line change
@@ -120,14 +120,14 @@ describe "YAML" do

describe "dump" do
it "returns YAML as a string" do
YAML.dump(%w(1 2 3)).should eq("--- \n- 1\n- 2\n- 3")
YAML.dump(%w(1 2 3)).should eq("---\n- 1\n- 2\n- 3\n")
end

it "writes YAML to a stream" do
string = String.build do |str|
YAML.dump(%w(1 2 3), str)
end
string.should eq("--- \n- 1\n- 2\n- 3")
string.should eq("---\n- 1\n- 2\n- 3\n")
end
end
end
8 changes: 6 additions & 2 deletions src/compiler/crystal/syntax/parser.cr
Original file line number Diff line number Diff line change
@@ -396,7 +396,7 @@ module Crystal
if atomic.is_a?(Call) && atomic.name != "[]" && !@def_vars.last.includes?(atomic.name)
raise "'#{@token.type}' before definition of '#{atomic.name}'"

atomic = Var.new(atomic.name)
atomic = Var.new(atomic.name).at(location)
end

push_var atomic
@@ -704,7 +704,11 @@ module Crystal
method = @token.type.to_s.byte_slice(0, @token.type.to_s.size - 1)
next_token_skip_space_or_newline
value = parse_op_assign
atomic = Call.new(atomic, "#{name}=", [Call.new(Call.new(atomic.clone, name, name_column_number: name_column_number), method, [value] of ASTNode, name_column_number: name_column_number)] of ASTNode, name_column_number: name_column_number).at(location)
atomic = Call.new(atomic, "#{name}=", [
Call.new(
Call.new(atomic.clone, name, name_column_number: name_column_number).at(location),
method, [value] of ASTNode, name_column_number: name_column_number).at(location),
] of ASTNode, name_column_number: name_column_number).at(location)
next
when :"||="
# Rewrite 'f.x ||= value' as 'f.x || f.x=(value)'
10 changes: 5 additions & 5 deletions src/io/console.cr
Original file line number Diff line number Diff line change
@@ -57,18 +57,18 @@ class IO::FileDescriptor
end

macro cooked_from_tc_mode!
mode.c_iflag |= Termios::InputMode::BRKINT |
mode.c_iflag |= (Termios::InputMode::BRKINT |
Termios::InputMode::ISTRIP |
Termios::InputMode::ICRNL |
Termios::InputMode::IXON
mode.c_oflag |= Termios::OutputMode::OPOST
mode.c_lflag |= Termios::LocalMode::ECHO |
Termios::InputMode::IXON).value
mode.c_oflag |= Termios::OutputMode::OPOST.value
mode.c_lflag |= (Termios::LocalMode::ECHO |
Termios::LocalMode::ECHOE |
Termios::LocalMode::ECHOK |
Termios::LocalMode::ECHONL |
Termios::LocalMode::ICANON |
Termios::LocalMode::ISIG |
Termios::LocalMode::IEXTEN
Termios::LocalMode::IEXTEN).value
LibC.tcsetattr(fd, Termios::LineControl::TCSANOW, pointerof(mode))
end

19 changes: 6 additions & 13 deletions src/yaml.cr
Original file line number Diff line number Diff line change
@@ -40,8 +40,11 @@ require "./yaml/*"
# File.open("file.yml", "w") { |f| {hello: "world"}.to_yaml(f) } # => writes it to the file
# ```
module YAML
class Error < Exception
end

# Exception thrown on a YAML parse error.
class ParseException < Exception
class ParseException < Error
getter line_number : Int32
getter column_number : Int32

@@ -84,12 +87,7 @@ module YAML
# # => }
# ```
def self.parse(data : String | IO) : Any
parser = YAML::Parser.new(data)
begin
parser.parse
ensure
parser.close
end
YAML::Parser.new data, &.parse
end

# Deserializes multiple YAML documents.
@@ -108,12 +106,7 @@ module YAML
# # => [{"foo" => "bar"}, {"hello" => "world"}]
# ```
def self.parse_all(data : String) : Array(Any)
parser = YAML::Parser.new(data)
begin
parser.parse_all
ensure
parser.close
end
YAML::Parser.new data, &.parse_all
end

# Serializes an object to YAML, returning it as a string.
110 changes: 110 additions & 0 deletions src/yaml/emitter.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
class YAML::Emitter
protected getter io

def initialize(@io : IO)
@emitter = Pointer(Void).malloc(LibYAML::EMITTER_SIZE).as(LibYAML::Emitter*)
@event = LibYAML::Event.new
@closed = false
LibYAML.yaml_emitter_initialize(@emitter)
LibYAML.yaml_emitter_set_output(@emitter, ->(data, buffer, size) {
emitter = data.as(YAML::Emitter)
emitter.io.write(Slice.new(buffer, size))
1
}, self.as(Void*))
end

def self.new(io : IO)
emitter = new(io)
yield emitter ensure emitter.close
end

def stream_start
emit stream_start, LibYAML::Encoding::UTF8
end

def stream_end
emit stream_end
end

def stream
stream_start
yield
stream_end
end

def document_start
emit document_start, nil, nil, nil, 0
end

def document_end
emit document_end, 1
end

def document
document_start
yield
document_end
end

def scalar(string)
emit scalar, nil, nil, string, string.bytesize, 1, 1, LibYAML::ScalarStyle::ANY
end

def <<(value)
scalar value.to_s
end

def sequence_start
emit sequence_start, nil, nil, 0, LibYAML::SequenceStyle::ANY
end

def sequence_end
emit sequence_end
end

def sequence
sequence_start
yield
sequence_end
end

def mapping_start
emit mapping_start, nil, nil, 0, LibYAML::MappingStyle::ANY
end

def mapping_end
emit mapping_end
end

def mapping
mapping_start
yield
mapping_end
end

def flush
LibYAML.yaml_emitter_flush(@emitter)
end

def finalize
return if @closed
LibYAML.yaml_emitter_delete(@emitter)
end

def close
finalize
@closed = true
end

macro emit(event_name, *args)
LibYAML.yaml_{{event_name}}_event_initialize(pointerof(@event), {{*args}})
yaml_emit({{event_name.stringify}})
end

private def yaml_emit(event_name)
ret = LibYAML.yaml_emitter_emit(@emitter, pointerof(@event))
if ret != 1
raise YAML::Error.new("error emitting #{event_name}")
end
end
end
20 changes: 11 additions & 9 deletions src/yaml/from_yaml.cr
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
def Object.from_yaml(string : String) : self
parser = YAML::PullParser.new(string)
parser.read_stream do
parser.read_document do
new parser
YAML::PullParser.new(string) do |parser|
parser.read_stream do
parser.read_document do
new parser
end
end
end
end

def Array.from_yaml(string : String)
parser = YAML::PullParser.new(string)
parser.read_stream do
parser.read_document do
new(parser) do |element|
yield element
YAML::PullParser.new(string) do |parser|
parser.read_stream do
parser.read_document do
new(parser) do |element|
yield element
end
end
end
end
57 changes: 47 additions & 10 deletions src/yaml/lib_yaml.cr
Original file line number Diff line number Diff line change
@@ -2,15 +2,27 @@
lib LibYAML
alias Int = LibC::Int

PARSER_SIZE = 480
type Parser = Void*

# The struct of yaml_parser_s is internal, yet some libraries (like Ruby's psych)
# access some of its data for getting the line/column information of an error.
# Here we replicate only part of this data. When we need it, we cast a Parser*
# to this type, so we don't need to have the correct size of the Parser struct,
# but only define some members at the beginning.
struct InternalParser
# To avoid mapping the whole parser and emitter structs,
# we computed their size with C programs. We then allocate
# the necessary memory and cast to the Parser and Emitter
# structs if necessary, where we mapped only some fields
# we are interested in.
{% if flag?(:x86_64) %}
PARSER_SIZE = 480
EMITTER_SIZE = 432
{% else %}
PARSER_SIZE = 248
EMITTER_SIZE = 264
{% end %}

enum Encoding
Any
UTF8
UTF16LE
UTF16BE
end

struct Parser
error : Int
problem : LibC::Char*
problem_offset : LibC::SizeT
@@ -131,12 +143,37 @@ lib LibYAML
end_mark : Mark
end

alias ReadHandler = Void*, LibC::UChar*, LibC::SizeT, LibC::SizeT* -> LibC::Int
alias ReadHandler = Void*, LibC::UChar*, LibC::SizeT, LibC::SizeT* -> Int

struct Emitter
error : Int
end

alias WriteHandler = (Void*, LibC::Char*, LibC::SizeT) -> Int

fun yaml_parser_initialize(parser : Parser*) : Int
fun yaml_parser_set_input(parser : Parser*, handler : ReadHandler, data : Void*)
fun yaml_parser_set_input_string(parser : Parser*, input : UInt8*, length : LibC::SizeT)
fun yaml_parser_parse(parser : Parser*, event : Event*) : Int
fun yaml_parser_delete(parser : Parser*)
fun yaml_event_delete(event : Event*)

fun yaml_emitter_initialize(emitter : Emitter*) : Int
fun yaml_emitter_set_output(emitter : Emitter*, handler : WriteHandler, data : Void*)
fun yaml_emitter_open(emitter : Emitter*) : Int
fun yaml_stream_start_event_initialize(event : Event*, encoding : Encoding) : Int
fun yaml_stream_end_event_initialize(event : Event*) : Int
fun yaml_document_start_event_initialize(event : Event*, version : VersionDirective*, tag_start : TagDirective*, tag_end : TagDirective*, implicit : Int) : Int
fun yaml_document_end_event_initialize(event : Event*, implicit : Int) : Int
fun yaml_scalar_event_initialize(event : Event*, anchor : LibC::Char*,
tag : LibC::Char*, value : LibC::Char*, length : Int,
plain_implicit : Int, quoted_implicit : Int, style : ScalarStyle) : Int
fun yaml_alias_event_initialize(event : Event*, anchor : LibC::Char*) : Int
fun yaml_sequence_start_event_initialize(event : Event*, anchor : LibC::Char*, tag : LibC::Char*, implicit : Int, style : SequenceStyle) : Int
fun yaml_sequence_end_event_initialize(event : Event*)
fun yaml_mapping_start_event_initialize(event : Event*, anchor : LibC::Char*, tag : LibC::Char*, implicit : Int, style : MappingStyle) : Int
fun yaml_mapping_end_event_initialize(event : Event*) : Int
fun yaml_emitter_emit(emitter : Emitter*, event : Event*) : Int
fun yaml_emitter_delete(emitter : Emitter*)
fun yaml_emitter_flush(emitter : Emitter*) : Int
end
5 changes: 5 additions & 0 deletions src/yaml/parser.cr
Original file line number Diff line number Diff line change
@@ -4,6 +4,11 @@ class YAML::Parser
@anchors = {} of String => YAML::Type
end

def self.new(content)
parser = new(content)
yield parser ensure parser.close
end

def close
@pull_parser.close
end
Loading