1
0

Merge branch 'main' into gui

This commit is contained in:
Jeff 2023-12-31 11:49:58 -05:00
commit 318825d1b1
18 changed files with 11624 additions and 11554 deletions

View File

@ -4,8 +4,8 @@ new_data = []
for commit_data in data { for commit_data in data {
new_data += { new_data += {
message = commit_data.commit.message message = commit_data:commit:message
name = commit_data.commit.committer.name name = commit_data:commit:committer:name
} }
} }

View File

@ -58,10 +58,6 @@ impl AbstractTree for Block {
.into_par_iter() .into_par_iter()
.enumerate() .enumerate()
.find_map_first(|(index, statement)| { .find_map_first(|(index, statement)| {
if let Statement::Return(expression) = statement {
return Some(expression.run(source, context));
}
let result = statement.run(source, context); let result = statement.run(source, context);
if result.is_err() { if result.is_err() {
@ -79,10 +75,6 @@ impl AbstractTree for Block {
let mut prev_result = None; let mut prev_result = None;
for statement in &self.statements { for statement in &self.statements {
if let Statement::Return(expression) = statement {
return expression.run(source, context);
}
prev_result = Some(statement.run(source, context)); prev_result = Some(statement.run(source, context));
} }
@ -91,10 +83,6 @@ impl AbstractTree for Block {
} }
fn expected_type(&self, context: &Map) -> Result<Type> { fn expected_type(&self, context: &Map) -> Result<Type> {
if self.is_async {
Ok(Type::Any)
} else {
self.statements.last().unwrap().expected_type(context) self.statements.last().unwrap().expected_type(context)
} }
} }
}

View File

@ -30,6 +30,12 @@ impl AbstractTree for Identifier {
let text = &source[node.byte_range()]; let text = &source[node.byte_range()];
if text.is_empty() {
println!("{node:?}");
}
debug_assert!(!text.is_empty());
Ok(Identifier(text.to_string())) Ok(Identifier(text.to_string()))
} }

View File

@ -21,7 +21,6 @@ pub mod r#match;
pub mod math; pub mod math;
pub mod statement; pub mod statement;
pub mod type_definition; pub mod type_definition;
pub mod r#use;
pub mod value_node; pub mod value_node;
pub mod r#while; pub mod r#while;
pub mod r#yield; pub mod r#yield;
@ -29,8 +28,7 @@ pub mod r#yield;
pub use { pub use {
assignment::*, block::*, expression::*, function_call::*, function_expression::*, assignment::*, block::*, expression::*, function_call::*, function_expression::*,
identifier::*, if_else::*, index::*, index_assignment::IndexAssignment, logic::*, math::*, identifier::*, if_else::*, index::*, index_assignment::IndexAssignment, logic::*, math::*,
r#for::*, r#match::*, r#use::*, r#while::*, r#yield::*, statement::*, type_definition::*, r#for::*, r#match::*, r#while::*, r#yield::*, statement::*, type_definition::*, value_node::*,
value_node::*,
}; };
use tree_sitter::Node; use tree_sitter::Node;

View File

@ -3,21 +3,19 @@ use tree_sitter::Node;
use crate::{ use crate::{
AbstractTree, Assignment, Block, Error, Expression, For, IfElse, IndexAssignment, Map, Match, AbstractTree, Assignment, Block, Error, Expression, For, IfElse, IndexAssignment, Map, Match,
Result, Type, Use, Value, While, Result, Type, Value, While,
}; };
/// Abstract representation of a statement. /// Abstract representation of a statement.
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)] #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
pub enum Statement { pub enum Statement {
Assignment(Box<Assignment>), Assignment(Box<Assignment>),
Return(Expression),
Expression(Expression), Expression(Expression),
IfElse(Box<IfElse>), IfElse(Box<IfElse>),
Match(Match), Match(Match),
While(Box<While>), While(Box<While>),
Block(Box<Block>), Block(Box<Block>),
For(Box<For>), For(Box<For>),
Use(Use),
IndexAssignment(Box<IndexAssignment>), IndexAssignment(Box<IndexAssignment>),
} }
@ -31,15 +29,6 @@ impl AbstractTree for Statement {
"assignment" => Ok(Statement::Assignment(Box::new( "assignment" => Ok(Statement::Assignment(Box::new(
Assignment::from_syntax_node(source, child, context)?, Assignment::from_syntax_node(source, child, context)?,
))), ))),
"return" => {
let expression_node = child.child(1).unwrap();
Ok(Statement::Return(Expression::from_syntax_node(
source,
expression_node,
context,
)?))
}
"expression" => Ok(Statement::Expression(Expression::from_syntax_node( "expression" => Ok(Statement::Expression(Expression::from_syntax_node(
source, child, context, source, child, context,
)?)), )?)),
@ -55,9 +44,6 @@ impl AbstractTree for Statement {
"for" => Ok(Statement::For(Box::new(For::from_syntax_node( "for" => Ok(Statement::For(Box::new(For::from_syntax_node(
source, child, context, source, child, context,
)?))), )?))),
"use" => Ok(Statement::Use(Use::from_syntax_node(
source, child, context,
)?)),
"index_assignment" => Ok(Statement::IndexAssignment(Box::new( "index_assignment" => Ok(Statement::IndexAssignment(Box::new(
IndexAssignment::from_syntax_node(source, child, context)?, IndexAssignment::from_syntax_node(source, child, context)?,
))), ))),
@ -77,14 +63,12 @@ impl AbstractTree for Statement {
fn run(&self, source: &str, context: &Map) -> Result<Value> { fn run(&self, source: &str, context: &Map) -> Result<Value> {
match self { match self {
Statement::Assignment(assignment) => assignment.run(source, context), Statement::Assignment(assignment) => assignment.run(source, context),
Statement::Return(expression) => expression.run(source, context),
Statement::Expression(expression) => expression.run(source, context), Statement::Expression(expression) => expression.run(source, context),
Statement::IfElse(if_else) => if_else.run(source, context), Statement::IfElse(if_else) => if_else.run(source, context),
Statement::Match(r#match) => r#match.run(source, context), Statement::Match(r#match) => r#match.run(source, context),
Statement::While(r#while) => r#while.run(source, context), Statement::While(r#while) => r#while.run(source, context),
Statement::Block(block) => block.run(source, context), Statement::Block(block) => block.run(source, context),
Statement::For(r#for) => r#for.run(source, context), Statement::For(r#for) => r#for.run(source, context),
Statement::Use(run) => run.run(source, context),
Statement::IndexAssignment(index_assignment) => index_assignment.run(source, context), Statement::IndexAssignment(index_assignment) => index_assignment.run(source, context),
} }
} }
@ -92,14 +76,12 @@ impl AbstractTree for Statement {
fn expected_type(&self, context: &Map) -> Result<Type> { fn expected_type(&self, context: &Map) -> Result<Type> {
match self { match self {
Statement::Assignment(assignment) => assignment.expected_type(context), Statement::Assignment(assignment) => assignment.expected_type(context),
Statement::Return(expression) => expression.expected_type(context),
Statement::Expression(expression) => expression.expected_type(context), Statement::Expression(expression) => expression.expected_type(context),
Statement::IfElse(if_else) => if_else.expected_type(context), Statement::IfElse(if_else) => if_else.expected_type(context),
Statement::Match(r#match) => r#match.expected_type(context), Statement::Match(r#match) => r#match.expected_type(context),
Statement::While(r#while) => r#while.expected_type(context), Statement::While(r#while) => r#while.expected_type(context),
Statement::Block(block) => block.expected_type(context), Statement::Block(block) => block.expected_type(context),
Statement::For(r#for) => r#for.expected_type(context), Statement::For(r#for) => r#for.expected_type(context),
Statement::Use(r#use) => r#use.expected_type(context),
Statement::IndexAssignment(index_assignment) => index_assignment.expected_type(context), Statement::IndexAssignment(index_assignment) => index_assignment.expected_type(context),
} }
} }

View File

@ -1,44 +0,0 @@
use std::fs::read_to_string;
use serde::{Deserialize, Serialize};
use tree_sitter::Node;
use crate::{interpret_with_context, AbstractTree, Error, Map, Result, Type, Value};
/// Abstract representation of a use statement.
///
/// Use will evaluate the Dust file at the given path. It will create an empty
/// context to do so, then apply every value from that context to the current
/// context.
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
pub struct Use {
path: String,
}
impl AbstractTree for Use {
fn from_syntax_node(source: &str, node: Node, _context: &Map) -> Result<Self> {
Error::expect_syntax_node(source, "use", node)?;
let string_node = node.child(1).unwrap();
let path = source[string_node.start_byte() + 1..string_node.end_byte() - 1].to_string();
Ok(Use { path })
}
fn run(&self, _source: &str, context: &Map) -> Result<Value> {
let file_contents = read_to_string(&self.path)?;
let file_context = Map::new();
interpret_with_context(&file_contents, file_context.clone())?;
for (key, (value, r#type)) in file_context.variables()?.iter() {
context.set(key.clone(), value.clone(), Some(r#type.clone()))?;
}
Ok(Value::Map(file_context))
}
fn expected_type(&self, _context: &Map) -> Result<Type> {
Ok(Type::Map)
}
}

View File

@ -84,14 +84,8 @@ impl BuiltInFunction for Append {
} }
fn run(&self, arguments: &[Value], _context: &Map) -> Result<Value> { fn run(&self, arguments: &[Value], _context: &Map) -> Result<Value> {
let file_content = arguments let file_content = arguments.get(0).unwrap_or_default().as_string()?;
.first() let path = arguments.get(1).unwrap_or_default().as_string()?;
.unwrap_or(&Value::none())
.as_string()?;
let path = arguments
.get(1)
.unwrap_or(&Value::none())
.as_string()?;
File::options() File::options()
.append(true) .append(true)

View File

@ -401,10 +401,14 @@ impl fmt::Display for Error {
actual, actual,
location, location,
relevant_source, relevant_source,
} => write!( } => {
let location = get_position(location);
write!(
f, f,
"Expected {expected}, but got {actual} at {location}. Code: {relevant_source} ", "Expected {expected}, but got {actual} at {location}. Code: {relevant_source} ",
), )
}
WrongColumnAmount { expected, actual } => write!( WrongColumnAmount { expected, actual } => write!(
f, f,
"Wrong column amount. Expected {expected} but got {actual}." "Wrong column amount. Expected {expected} but got {actual}."
@ -412,7 +416,9 @@ impl fmt::Display for Error {
External(message) => write!(f, "External error: {message}"), External(message) => write!(f, "External error: {message}"),
CustomMessage(message) => write!(f, "{message}"), CustomMessage(message) => write!(f, "{message}"),
Syntax { source, location } => { Syntax { source, location } => {
write!(f, "Syntax error at {location}, this is not valid: {source}") let location = get_position(location);
write!(f, "Syntax error at {location}: {source}")
} }
TypeCheck { expected, actual } => write!( TypeCheck { expected, actual } => write!(
f, f,
@ -422,7 +428,11 @@ impl fmt::Display for Error {
error, error,
location, location,
source, source,
} => write!(f, "{error} Occured at {location}: \"{source}\""), } => {
let location = get_position(location);
write!(f, "{error} Occured at {location}: \"{source}\"")
}
SerdeJson(message) => write!(f, "JSON processing error: {message}"), SerdeJson(message) => write!(f, "JSON processing error: {message}"),
ParserCancelled => write!( ParserCancelled => write!(
f, f,
@ -431,3 +441,7 @@ impl fmt::Display for Error {
} }
} }
} }
fn get_position(position: &Point) -> String {
format!("column {}, row {}", position.row + 1, position.column)
}

View File

@ -2,7 +2,7 @@
//! //!
//! You can use this library externally by calling either of the "eval" //! You can use this library externally by calling either of the "eval"
//! functions or by constructing your own Evaluator. //! functions or by constructing your own Evaluator.
use tree_sitter::{Parser, Tree as TSTree}; use tree_sitter::{Node, Parser, Tree as TSTree, TreeCursor};
use crate::{language, AbstractTree, Error, Map, Result, Root, Value}; use crate::{language, AbstractTree, Error, Map, Result, Root, Value};
@ -71,12 +71,39 @@ impl Interpreter {
} }
} }
pub fn parse_only(&mut self, source: &str) { pub fn parse(&mut self, source: &str) -> Result<()> {
self.syntax_tree = self.parser.parse(source, None); fn check_for_error(source: &str, node: Node, cursor: &mut TreeCursor) -> Result<()> {
if node.is_error() {
Err(Error::Syntax {
source: source[node.byte_range()].to_string(),
location: node.start_position(),
})
} else {
for child in node.children(&mut cursor.clone()) {
check_for_error(source, child, cursor)?;
}
Ok(())
}
}
let syntax_tree = self.parser.parse(source, None);
if let Some(tree) = &syntax_tree {
let root = tree.root_node();
let mut cursor = root.walk();
check_for_error(source, root, &mut cursor)?;
}
self.syntax_tree = syntax_tree;
Ok(())
} }
pub fn run(&mut self, source: &str) -> Result<Value> { pub fn run(&mut self, source: &str) -> Result<Value> {
self.syntax_tree = self.parser.parse(source, None); self.parse(source)?;
self.abstract_tree = if let Some(syntax_tree) = &self.syntax_tree { self.abstract_tree = if let Some(syntax_tree) = &self.syntax_tree {
Some(Root::from_syntax_node( Some(Root::from_syntax_node(
source, source,

View File

@ -75,7 +75,7 @@ fn main() {
let mut interpreter = Interpreter::new(context); let mut interpreter = Interpreter::new(context);
if args.show_syntax_tree { if args.show_syntax_tree {
interpreter.parse_only(&source); interpreter.parse(&source).unwrap();
println!("{}", interpreter.syntax_tree().unwrap()); println!("{}", interpreter.syntax_tree().unwrap());
} }

2
test.ds Normal file
View File

@ -0,0 +1,2 @@
fn = (x <int>) <none> {}
fn(1)

View File

@ -76,19 +76,6 @@ mod for_loop {
result result
); );
} }
#[test]
fn modify_value_async() {
let result = interpret(
"
fn = (x <int>) <none> {}
fn(1)
",
);
assert_eq!(Ok(Value::none()), result);
}
} }
mod logic { mod logic {
@ -107,27 +94,27 @@ mod value {
use dust_lang::*; use dust_lang::*;
#[test] #[test]
fn interpret_empty() { fn empty() {
assert_eq!(interpret("x = 9"), Ok(Value::Option(None))); assert_eq!(interpret("x = 9"), Ok(Value::Option(None)));
assert_eq!(interpret("x = 1 + 1"), Ok(Value::Option(None))); assert_eq!(interpret("x = 1 + 1"), Ok(Value::Option(None)));
} }
#[test] #[test]
fn interpret_integer() { fn integer() {
assert_eq!(interpret("1"), Ok(Value::Integer(1))); assert_eq!(interpret("1"), Ok(Value::Integer(1)));
assert_eq!(interpret("123"), Ok(Value::Integer(123))); assert_eq!(interpret("123"), Ok(Value::Integer(123)));
assert_eq!(interpret("-666"), Ok(Value::Integer(-666))); assert_eq!(interpret("-666"), Ok(Value::Integer(-666)));
} }
#[test] #[test]
fn interpret_float() { fn float() {
assert_eq!(interpret("0.1"), Ok(Value::Float(0.1))); assert_eq!(interpret("0.1"), Ok(Value::Float(0.1)));
assert_eq!(interpret("12.3"), Ok(Value::Float(12.3))); assert_eq!(interpret("12.3"), Ok(Value::Float(12.3)));
assert_eq!(interpret("-6.66"), Ok(Value::Float(-6.66))); assert_eq!(interpret("-6.66"), Ok(Value::Float(-6.66)));
} }
#[test] #[test]
fn interpret_string() { fn string() {
assert_eq!(interpret("\"one\""), Ok(Value::String("one".to_string()))); assert_eq!(interpret("\"one\""), Ok(Value::String("one".to_string())));
assert_eq!(interpret("'one'"), Ok(Value::String("one".to_string()))); assert_eq!(interpret("'one'"), Ok(Value::String("one".to_string())));
assert_eq!(interpret("`one`"), Ok(Value::String("one".to_string()))); assert_eq!(interpret("`one`"), Ok(Value::String("one".to_string())));
@ -140,7 +127,7 @@ mod value {
} }
#[test] #[test]
fn interpret_list() { fn list() {
assert_eq!( assert_eq!(
interpret("[1, 2, 'foobar']"), interpret("[1, 2, 'foobar']"),
Ok(Value::List(List::with_items(vec![ Ok(Value::List(List::with_items(vec![
@ -152,7 +139,7 @@ mod value {
} }
#[test] #[test]
fn interpret_map() { fn map() {
let map = Map::new(); let map = Map::new();
map.set("x".to_string(), Value::Integer(1), None).unwrap(); map.set("x".to_string(), Value::Integer(1), None).unwrap();
@ -163,7 +150,7 @@ mod value {
} }
#[test] #[test]
fn interpret_map_types() { fn map_types() {
let map = Map::new(); let map = Map::new();
map.set("x".to_string(), Value::Integer(1), Some(Type::Integer)) map.set("x".to_string(), Value::Integer(1), Some(Type::Integer))
@ -182,7 +169,7 @@ mod value {
} }
#[test] #[test]
fn interpret_map_type_errors() { fn map_type_errors() {
assert!(interpret("{ foo <bool> = 'bar' }") assert!(interpret("{ foo <bool> = 'bar' }")
.unwrap_err() .unwrap_err()
.is_type_check_error(&Error::TypeCheck { .is_type_check_error(&Error::TypeCheck {
@ -192,15 +179,15 @@ mod value {
} }
#[test] #[test]
fn interpret_function() { fn function() {
let result = interpret("() -> <int> { 1 }"); let result = interpret("() <int> { 1 }");
let value = result.unwrap(); let value = result.unwrap();
let function = value.as_function().unwrap(); let function = value.as_function().unwrap();
assert_eq!(&Vec::<Identifier>::with_capacity(0), function.parameters()); assert_eq!(&Vec::<Identifier>::with_capacity(0), function.parameters());
assert_eq!(&Type::Integer, function.return_type()); assert_eq!(&Type::Integer, function.return_type());
let result = interpret("(x <bool>) -> <bool> {true}"); let result = interpret("(x <bool>) <bool> { true }");
let value = result.unwrap(); let value = result.unwrap();
let function = value.as_function().unwrap(); let function = value.as_function().unwrap();
@ -212,7 +199,7 @@ mod value {
} }
#[test] #[test]
fn interpret_option() { fn option() {
let result = interpret("x <option(int)> = some(1); x").unwrap(); let result = interpret("x <option(int)> = some(1); x").unwrap();
assert_eq!(Value::Option(Some(Box::new(Value::Integer(1)))), result); assert_eq!(Value::Option(Some(Box::new(Value::Integer(1)))), result);
@ -223,11 +210,11 @@ mod function_call {
use dust_lang::*; use dust_lang::*;
#[test] #[test]
fn interpret_function_call() { fn function_call() {
assert_eq!( assert_eq!(
interpret( interpret(
" "
foobar = (message <str>) -> <str> { message } foobar = (message <str>) <str> { message }
foobar('Hiya') foobar('Hiya')
", ",
), ),
@ -236,7 +223,20 @@ mod function_call {
} }
#[test] #[test]
fn interpret_callback() { fn call_empty_function() {
assert_eq!(
interpret(
"
foobar = (message <str>) <none> {}
foobar('Hiya')
",
),
Ok(Value::none())
);
}
#[test]
fn callback() {
assert_eq!( assert_eq!(
interpret( interpret(
" "
@ -251,7 +251,7 @@ mod function_call {
} }
#[test] #[test]
fn interpret_built_in_function_call() { fn built_in_function_call() {
assert_eq!(interpret("output('Hiya')"), Ok(Value::Option(None))); assert_eq!(interpret("output('Hiya')"), Ok(Value::Option(None)));
} }
} }
@ -260,7 +260,7 @@ mod if_else {
use dust_lang::*; use dust_lang::*;
#[test] #[test]
fn interpret_if() { fn r#if() {
assert_eq!( assert_eq!(
interpret("if true { 'true' }"), interpret("if true { 'true' }"),
Ok(Value::String("true".to_string())) Ok(Value::String("true".to_string()))
@ -268,7 +268,7 @@ mod if_else {
} }
#[test] #[test]
fn interpret_if_else() { fn if_else() {
assert_eq!( assert_eq!(
interpret("if false { 1 } else { 2 }"), interpret("if false { 1 } else { 2 }"),
Ok(Value::Integer(2)) Ok(Value::Integer(2))
@ -280,7 +280,7 @@ mod if_else {
} }
#[test] #[test]
fn interpret_if_else_else_if_else() { fn if_else_else_if_else() {
assert_eq!( assert_eq!(
interpret( interpret(
" "
@ -298,7 +298,7 @@ mod if_else {
} }
#[test] #[test]
fn interpret_if_else_if_else_if_else_if_else() { fn if_else_if_else_if_else_if_else() {
assert_eq!( assert_eq!(
interpret( interpret(
" "
@ -342,13 +342,13 @@ mod index {
let test = interpret( let test = interpret(
" "
x = [1 2 3] x = [1 2 3]
y = () -> <int> { 0 } y = () <int> { 2 }
x:y() x:y()
", ",
) )
.unwrap(); .unwrap();
assert_eq!(Value::Integer(1), test); assert_eq!(Value::Integer(3), test);
} }
} }
@ -356,7 +356,7 @@ mod r#match {
use dust_lang::*; use dust_lang::*;
#[test] #[test]
fn interpret_match() { fn r#match() {
let test = interpret( let test = interpret(
" "
match 1 { match 1 {
@ -372,7 +372,7 @@ mod r#match {
} }
#[test] #[test]
fn interpret_match_assignment() { fn match_assignment() {
let test = interpret( let test = interpret(
" "
x = match 1 { x = match 1 {
@ -393,7 +393,7 @@ mod r#while {
use dust_lang::*; use dust_lang::*;
#[test] #[test]
fn interpret_while_loop() { fn while_loop() {
assert_eq!(interpret("while false { 'foo' }"), Ok(Value::Option(None))) assert_eq!(interpret("while false { 'foo' }"), Ok(Value::Option(None)))
} }

View File

@ -2,7 +2,7 @@
Anonymous Function Anonymous Function
================================================================================ ================================================================================
() <str> { "Hiya" } () -> <str> { "Hiya" }
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------

View File

@ -5,6 +5,7 @@ Simple Identifiers
x x
_y _y
__xyz__ __xyz__
x123x
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@ -15,6 +16,9 @@ __xyz__
(statement (statement
(expression (expression
(identifier))) (identifier)))
(statement
(expression
(identifier)))
(statement (statement
(expression (expression
(identifier)))) (identifier))))

View File

@ -11,14 +11,6 @@ module.exports = grammar({
_comment: $ => /[#][^#\n]*[#|\n]/, _comment: $ => /[#][^#\n]*[#|\n]/,
block: $ =>
seq(
optional('async'),
'{',
repeat1($.statement),
'}',
),
statement: $ => statement: $ =>
prec.left( prec.left(
seq( seq(
@ -70,6 +62,14 @@ module.exports = grammar({
), ),
), ),
block: $ =>
seq(
optional('async'),
'{',
repeat($.statement),
'}',
),
identifier: $ => identifier: $ =>
choice( choice(
$._identifier_pattern, $._identifier_pattern,
@ -77,7 +77,7 @@ module.exports = grammar({
), ),
_identifier_pattern: $ => _identifier_pattern: $ =>
/[_a-zA-Z]+[_a-zA-Z0-9]?/, /[_a-zA-Z]+[_a-zA-Z0-9]*[_a-zA-Z]?/,
value: $ => value: $ =>
choice( choice(
@ -175,7 +175,7 @@ module.exports = grammar({
map: $ => map: $ =>
seq( seq(
'{', '{',
repeat( repeat1(
seq( seq(
$.identifier, $.identifier,
optional($.type_definition), optional($.type_definition),

View File

@ -17,38 +17,6 @@
"type": "PATTERN", "type": "PATTERN",
"value": "[#][^#\\n]*[#|\\n]" "value": "[#][^#\\n]*[#|\\n]"
}, },
"block": {
"type": "SEQ",
"members": [
{
"type": "CHOICE",
"members": [
{
"type": "STRING",
"value": "async"
},
{
"type": "BLANK"
}
]
},
{
"type": "STRING",
"value": "{"
},
{
"type": "REPEAT1",
"content": {
"type": "SYMBOL",
"name": "statement"
}
},
{
"type": "STRING",
"value": "}"
}
]
},
"statement": { "statement": {
"type": "PREC_LEFT", "type": "PREC_LEFT",
"value": 0, "value": 0,
@ -202,6 +170,38 @@
} }
} }
}, },
"block": {
"type": "SEQ",
"members": [
{
"type": "CHOICE",
"members": [
{
"type": "STRING",
"value": "async"
},
{
"type": "BLANK"
}
]
},
{
"type": "STRING",
"value": "{"
},
{
"type": "REPEAT",
"content": {
"type": "SYMBOL",
"name": "statement"
}
},
{
"type": "STRING",
"value": "}"
}
]
},
"identifier": { "identifier": {
"type": "CHOICE", "type": "CHOICE",
"members": [ "members": [
@ -217,7 +217,7 @@
}, },
"_identifier_pattern": { "_identifier_pattern": {
"type": "PATTERN", "type": "PATTERN",
"value": "[_a-zA-Z]+[_a-zA-Z0-9]?" "value": "[_a-zA-Z]+[_a-zA-Z0-9]*[_a-zA-Z]?"
}, },
"value": { "value": {
"type": "CHOICE", "type": "CHOICE",
@ -518,7 +518,7 @@
"value": "{" "value": "{"
}, },
{ {
"type": "REPEAT", "type": "REPEAT1",
"content": { "content": {
"type": "SEQ", "type": "SEQ",
"members": [ "members": [

View File

@ -37,7 +37,7 @@
"fields": {}, "fields": {},
"children": { "children": {
"multiple": true, "multiple": true,
"required": true, "required": false,
"types": [ "types": [
{ {
"type": "statement", "type": "statement",
@ -365,7 +365,7 @@
"fields": {}, "fields": {},
"children": { "children": {
"multiple": true, "multiple": true,
"required": false, "required": true,
"types": [ "types": [
{ {
"type": "identifier", "type": "identifier",

File diff suppressed because it is too large Load Diff