diff --git a/base/block.aya b/base/block.aya index 7d0c85d8..e064e3b5 100644 --- a/base/block.aya +++ b/base/block.aya @@ -72,22 +72,6 @@ .# Utility Functions .##################### - - .# ::list _capture_vars\n given a list of symbols, return a dict with each symbol and it's assignment - .{ Example: - aya> {1 +}:a 2:b - {1 +} 2 - aya> [::a ::b].capture_vars - :{ - {1 +}:a; - 2:b; - } - aya> [::a ::b ::c].capture_vars - ERROR: Variable c not found - .} - { :{1, :# {$~\:=} } }:_capture_vars; - - .#? ::block .op\n return the block (allows use of (::sym or ::block) .op without type checking {}:op; diff --git a/examples/quicksort.aya b/examples/quicksort.aya index a06e8f10..a8bf73ef 100644 --- a/examples/quicksort.aya +++ b/examples/quicksort.aya @@ -42,7 +42,7 @@ lst qs :P {.E1> {$\; :&V$@\< @\ :&#! @:&@.i \@.i qss @@qss JJ} ?}:qss; .# Stack only version with no global vars -.# {.E1> {$\; :&V$@\< @\ :&#! @:&@.i \@.i _ @@_ JJ} ?} :{1, :_} :+ +.# {.E1> {$\; :&V$@\< @\ :&#! @:&@.i \@.i _ @@_ JJ} ?} :{_,} :+ .# Quicksort benchmark and example output .# aya> 10 R :# {; [100000,;.Q] {qs;}.time} :& .E \W\/ diff --git a/src/aya/instruction/DictLiteralInstruction.java b/src/aya/instruction/DictLiteralInstruction.java index 3d5bb384..bfc4628d 100644 --- a/src/aya/instruction/DictLiteralInstruction.java +++ b/src/aya/instruction/DictLiteralInstruction.java @@ -1,16 +1,16 @@ package aya.instruction; -import java.util.LinkedList; -import java.util.Queue; +import java.util.HashMap; import aya.ReprStream; -import aya.eval.ExecutionContext; import aya.eval.BlockEvaluator; -import aya.obj.Obj; +import aya.exceptions.runtime.EmptyStackError; import aya.obj.block.BlockUtils; import aya.obj.block.StaticBlock; import aya.obj.dict.Dict; +import aya.obj.symbol.Symbol; import aya.parser.SourceStringRef; +import aya.variable.VariableData; /** DictFactories sit on the instruction stack. When evoked, they generate a dict * given the current scope of variables @@ -20,65 +20,72 @@ public class DictLiteralInstruction extends Instruction { StaticBlock _block; - private int num_captures; + HashMap _defaults; public DictLiteralInstruction(SourceStringRef source, StaticBlock b) { - super(source); - this._block = b; - this.num_captures = 0; + this(source, b, null); } - public DictLiteralInstruction(SourceStringRef source, StaticBlock b, int num_captures) { + public DictLiteralInstruction(SourceStringRef source, StaticBlock b, HashMap defaults) { super(source); this._block = b; - this.num_captures = num_captures; - } - - public int numCaptures() { - return num_captures; + this._defaults = defaults; } - - /** Run the dict, collect variables, return the Dict object */ - public Dict getDict(ExecutionContext context, Queue q) { - //Add the variable set to the stack - context.getVars().add(new Dict(), true); + + @Override + public void execute(BlockEvaluator b) { + final VariableData vars = b.getContext().getVars(); + + // Add the variable set to the stack, true: capture all assignments + Dict dict_scope = new Dict(); + vars.add(dict_scope, true); - //Run the blockEvaluator - BlockEvaluator evaluator = context.createEvaluator(); - if (q != null) { - while (!q.isEmpty()) { - evaluator.push(q.poll()); + // If there are defaults, add them to the dict scope + if (_defaults != null) { + for (Symbol var : _defaults.keySet()) { + BlockEvaluator evaluator = b.getContext().createEvaluator(); + evaluator.dump(_defaults.get(var)); + evaluator.eval(); + dict_scope.set(var, evaluator.pop()); } } - evaluator.dump(_block); - evaluator.eval(); + + // Run block in an isolated evaluator so we can copy the variables once the block is finished + BlockEvaluator evaluator = b.getContext().createEvaluator(); + + final int num_args = _block.getNumArgs(); + final boolean has_locals = _block.hasLocals(); - //Retrieve the Dict - return context.getVars().popGet(); - } - - @Override - public void execute(BlockEvaluator b) { - Queue q = null; - int n = this.numCaptures(); - if (n > 0) { - q = new LinkedList(); - for (int i = 0; i < n; i++) { - q.add(b.pop()); + // Copy stack args into evaluator (preserve order) + if (num_args > 0) { + if (b.getStack().size() < num_args) { + throw new EmptyStackError("Empty stack at dict literal"); } + final int offset = b.getStack().size() - 1; + for (int i = num_args-1; i >= 0; i--) evaluator.push(b.getStack().get(offset - i)); + for (int i = 0; i < num_args; i++) b.pop(); } - b.push(this.getDict(b.getContext(), q)); + evaluator.dump(_block); + + // If we have locals, we will want to pull the data out if it before they are popped + // so we remove the instruction to pop the variables, we will do it ourselves below + if (has_locals) evaluator.getInstructions().getInstrucionList().remove(0); + + evaluator.eval(); + + if (has_locals) { + Dict block_locals = vars.popGet(); + dict_scope.update(block_locals); + } + + // Pop the dict scope and add it to the stack + b.push(vars.popGet()); // popGet returns dict_scope } @Override public ReprStream repr(ReprStream stream) { - if (num_captures == 0) { - stream.print(":{"); - } else { - stream.print("{" + num_captures + ","); - } - BlockUtils.repr(stream, _block, false, null); - stream.print("}"); + stream.print(":"); + BlockUtils.repr(stream, _block, true, null); return stream; } } diff --git a/src/aya/instruction/EmptyDictLiteralInstruction.java b/src/aya/instruction/EmptyDictLiteralInstruction.java index 4b10f074..cc132d2a 100644 --- a/src/aya/instruction/EmptyDictLiteralInstruction.java +++ b/src/aya/instruction/EmptyDictLiteralInstruction.java @@ -19,9 +19,6 @@ protected EmptyDictLiteralInstruction() { super(null, StaticBlock.EMPTY); } - @Override - public int numCaptures() { return 0; } - public Dict getDict(ExecutionContext context, Queue q) { return new Dict(); } diff --git a/src/aya/obj/block/StaticBlock.java b/src/aya/obj/block/StaticBlock.java index f9c308c5..528cb5fe 100644 --- a/src/aya/obj/block/StaticBlock.java +++ b/src/aya/obj/block/StaticBlock.java @@ -58,6 +58,14 @@ public boolean hasLocals() { return _locals != null; } + public int getNumArgs() { + if (_args == null) { + return 0; + } else { + return _args.size(); + } + } + ////////////////////// // Used By BlockOps // ////////////////////// diff --git a/src/aya/parser/HeaderUtils.java b/src/aya/parser/HeaderUtils.java new file mode 100644 index 00000000..1c76c0b3 --- /dev/null +++ b/src/aya/parser/HeaderUtils.java @@ -0,0 +1,212 @@ +package aya.parser; + + +import java.util.ArrayList; +import java.util.HashMap; + +import aya.exceptions.parser.EndOfInputError; +import aya.exceptions.parser.ParserException; +import aya.exceptions.parser.SyntaxError; +import aya.instruction.Instruction; +import aya.instruction.variable.QuoteGetVariableInstruction; +import aya.instruction.variable.assignment.Assignment; +import aya.instruction.variable.assignment.SimpleAssignment; +import aya.instruction.variable.assignment.TypedAssignment; +import aya.instruction.variable.assignment.UnpackAssignment; +import aya.obj.block.BlockUtils; +import aya.obj.block.StaticBlock; +import aya.obj.dict.Dict; +import aya.obj.number.Num; +import aya.obj.symbol.Symbol; +import aya.parser.token.TokenQueue; +import aya.parser.tokens.LambdaToken; +import aya.parser.tokens.ListToken; +import aya.parser.tokens.OperatorToken; +import aya.parser.tokens.SymbolToken; +import aya.parser.tokens.Token; +import aya.parser.tokens.VarToken; +import aya.util.Pair; +import aya.util.Triple; + +public class HeaderUtils { + + // args, locals, captures + public static Triple, Dict, HashMap> generateBlockHeader(TokenQueue tokens) throws ParserException { + Pair split_tokens = splitAtColon(tokens); + TokenQueue arg_tokens = split_tokens.first(); + TokenQueue locals_and_captures_tokens = split_tokens.second(); + + // Args + ArrayList args = generateBlockHeaderArgs(arg_tokens); + + // Locals & Captures + Pair> locals_and_captures = generateBlockHeaderDefaults(locals_and_captures_tokens); + Dict locals = locals_and_captures.first(); + HashMap captures = locals_and_captures.second(); + + // Null checks + if (args.size() == 0) args = null; + if (locals.size() == 0) locals = null; + if (captures.size() == 0) captures = null; + + return new Triple, Dict, HashMap>(args, locals, captures); + } + + private static ArrayList generateBlockHeaderArgs(TokenQueue tokens) throws ParserException { + ArrayList out = new ArrayList(); + while (tokens.hasNext()) { + Assignment arg = nextArg(tokens); + out.add(arg); + } + return out; + } + + private static Assignment nextArg(TokenQueue tokens) throws EndOfInputError, SyntaxError { + Token current = tokens.next(); + if (current.isa(Token.VAR)) { + VarToken var = (VarToken)current; + boolean copy = false; + Symbol arg_type = null; + + // Copy? + if (tokens.hasNext() && tokens.peek().isa(Token.OP) && tokens.peek().getData().equals("$")) { + tokens.next(); // Discard $ + copy = true; + } + + // Type annotation? + if (tokens.hasNext() && tokens.peek().isa(Token.SYMBOL)) { + SymbolToken sym_token = (SymbolToken)tokens.next(); + arg_type = sym_token.getSymbol(); + } + + if (copy || arg_type != null) { + return new TypedAssignment(var.getSourceStringRef(), var.getSymbol(), arg_type, copy); + } else { + return new SimpleAssignment(var.getSourceStringRef(), var.getSymbol()); + } + } else if (current.isa(Token.LIST)) { + ListToken unpack = (ListToken)current; + TokenQueue tq = new TokenQueue(unpack.getCol()); + ArrayList args = new ArrayList(); + Symbol catchall = null; + while (tq.hasNext()) { + // Catch-all + if (tq.peek().isa(Token.COLON)) { + Token colon = tq.next(); // colon + if (tq.hasNext() && tq.peek().isa(Token.VAR)) { + Token var = tq.next(); + catchall = ((VarToken)var).getSymbol(); + } else { + throw new SyntaxError("Expected varname after catchall assignment", colon.getSourceStringRef()); + } + + if (tq.hasNext()) { + throw new SyntaxError("Catch-all name must be last", current.getSourceStringRef()); + } + } else { + Assignment a = nextArg(tq); + boolean slurp = false; + if (tq.hasNext() && tq.peek().isa(Token.OP) && tq.peek().getData().equals("~")) { + slurp = true; + tq.next(); // Skip ~ + } + + args.add(new UnpackAssignment.Arg(a, slurp)); + } + } + + if (args.size() == 0) { + throw new SyntaxError("Unpack args must contain at least one element", current.getSourceStringRef()); + } else { + UnpackAssignment ua = UnpackAssignment.fromArgList(args, catchall, current.getSourceStringRef()); + return ua; + } + } else { + throw new SyntaxError("All arguments should follow the format name[$][::type]", current.getSourceStringRef()); + } + } + + /** Assumes args have already been set + * @param captures + * @throws ParserException */ + private static Pair> generateBlockHeaderDefaults(TokenQueue tokens) throws ParserException { + Dict locals = new Dict(); + HashMap captures = new HashMap(); + + while (tokens.hasNext()) { + Token current = tokens.next(); + if (current.isa(Token.VAR)) { + VarToken var = (VarToken)current; + if (!tokens.hasNext() || tokens.peek().isa(Token.VAR)) { + locals.set(var.getSymbol(), Num.ZERO); + } else if (tokens.peek().isa(Token.LAMBDA)){ + LambdaToken lambda = (LambdaToken)tokens.next(); + captures.put(var.getSymbol(), BlockUtils.fromIS(lambda.generateInstructionsForFirst())); + } else if (tokens.peek().isa(Token.OP)) { + OperatorToken opt = (OperatorToken)tokens.next(); + if (opt.getData().equals("^")) { + Instruction i = new QuoteGetVariableInstruction(current.getSourceStringRef(), var.getSymbol()); + StaticBlock b = BlockUtils.makeBlockWithSingleInstruction(i); + captures.put(var.getSymbol(), b); + } else { + generateBlockHeaderDefaultsError(current.getSourceStringRef()); + } + } else { + generateBlockHeaderDefaultsError(current.getSourceStringRef()); + } + } else { + generateBlockHeaderDefaultsError(current.getSourceStringRef()); + } + } + + return new Pair>(locals, captures); + } + + private static void generateBlockHeaderDefaultsError(SourceStringRef source) throws SyntaxError { + throw new SyntaxError("All variable initializers should follow the format name()", source); + } + + + + /** Split a single tokenQueue into two at the location of the colon + * t1 contains all tokens before the colon + * t2 contains all tokens after the colon + * both may be empty if there was nothing before/after the colon + * the colon is not included in any + * @param tokens + * @return + * @throws SyntaxError + */ + private static Pair splitAtColon(TokenQueue tokens) throws SyntaxError { + ArrayList ts = tokens.getArrayList(); + int colons = 0; + int colon_index = 0; + for (int i = 0; i < ts.size(); i++) { + if (ts.get(i).isa(Token.COLON)) { + colon_index = i; + colons++; + } + } + + if (colons == 0) { + return new Pair(tokens, new TokenQueue()); + } else if (colons > 1) { + throw new SyntaxError("Expected only one colon (:) token in blockEvaluator header", tokens.peek().getSourceStringRef()); + } else { + ArrayList t1 = new ArrayList(colon_index); + ArrayList t2 = new ArrayList(ts.size()-colon_index); + for (int i = 0; i < colon_index; i++) { + t1.add(ts.get(i)); + } + // colon_index+1 skip the colon itself + for (int i = colon_index+1; i < ts.size(); i++) { + t2.add(ts.get(i)); + } + + return new Pair(new TokenQueue(t1), + new TokenQueue(t2)); + } + } + +} diff --git a/src/aya/parser/tokens/BlockToken.java b/src/aya/parser/tokens/BlockToken.java index b851bccf..d125178a 100644 --- a/src/aya/parser/tokens/BlockToken.java +++ b/src/aya/parser/tokens/BlockToken.java @@ -3,27 +3,22 @@ import java.util.ArrayList; import java.util.HashMap; -import aya.exceptions.parser.EndOfInputError; import aya.exceptions.parser.ParserException; import aya.exceptions.parser.SyntaxError; import aya.instruction.BlockLiteralInstruction; import aya.instruction.Instruction; import aya.instruction.InstructionStack; -import aya.instruction.variable.QuoteGetVariableInstruction; import aya.instruction.variable.assignment.Assignment; -import aya.instruction.variable.assignment.SimpleAssignment; -import aya.instruction.variable.assignment.TypedAssignment; -import aya.instruction.variable.assignment.UnpackAssignment; import aya.obj.Obj; import aya.obj.block.BlockUtils; import aya.obj.block.StaticBlock; import aya.obj.dict.Dict; import aya.obj.number.Num; import aya.obj.symbol.Symbol; +import aya.parser.HeaderUtils; import aya.parser.Parser; import aya.parser.SourceStringRef; import aya.parser.token.TokenQueue; -import aya.util.Pair; import aya.util.Triple; public class BlockToken extends CollectionToken { @@ -48,41 +43,14 @@ public Instruction getInstruction() throws ParserException { if (blockData.size() == 2) { //Empty header, dict literal if (!header.hasNext()) { + // Eventually this may be used to specify that a block has locals + // For now it is an error to help transition from old dict literals throw new SyntaxError("Block cannot have an empty header", source); - //InstructionStack instructions = Parser.generate(blockData.get(1)); - //if (instructions.size() == 0) { - // return EmptyDictLiteralInstruction.INSTANCE; - //} else { - // return new DictLiteralInstruction(this.getSourceStringRef(), BlockUtils.fromIS(instructions)); - //} - } - // Single number in header, create a dict factory with a capture - else if (header.size() == 1 && header.peek() instanceof NumberToken) { - throw new SyntaxError("Block cannot have a number in header", source); - /* - NumberToken nt = (NumberToken)header.peek(); - int n = 0; - try { - n = nt.numValue().toInt(); - } catch (NumberFormatException e) { - throw new SyntaxError(nt + " is not a valid number in the blockEvaluator header", nt.getSourceStringRef()); - } - - if (n < 1) { - throw new SyntaxError("Cannot capture less than 1 elements from outer stack in a dict literal", nt.getSourceStringRef()); - } - InstructionStack instructions = Parser.generate(blockData.get(1)); - if (n == 0 && instructions.isEmpty()) { - return EmptyDictLiteralInstruction.INSTANCE; - } else { - return new DictLiteralInstruction(this.getSourceStringRef(), BlockUtils.fromIS(instructions), n); - } - */ } //Non-empty header, args and local variables else { InstructionStack main_instructions = Parser.generate(blockData.get(1)); - Triple, Dict, HashMap> p = generateBlockHeader(blockData.get(0)); + Triple, Dict, HashMap> p = HeaderUtils.generateBlockHeader(blockData.get(0)); StaticBlock blk = BlockUtils.fromIS(main_instructions, p.second(), p.first()); return new BlockLiteralInstruction(this.getSourceStringRef(), blk, p.third()); } @@ -92,184 +60,6 @@ else if (header.size() == 1 && header.peek() instanceof NumberToken) { } } - // args, locals, captures - private Triple, Dict, HashMap> generateBlockHeader(TokenQueue tokens) throws ParserException { - Pair split_tokens = splitAtColon(tokens); - TokenQueue arg_tokens = split_tokens.first(); - TokenQueue locals_and_captures_tokens = split_tokens.second(); - - // Args - ArrayList args = generateBlockHeaderArgs(arg_tokens); - - // Locals & Captures - Pair> locals_and_captures = generateBlockHeaderDefaults(locals_and_captures_tokens); - Dict locals = locals_and_captures.first(); - HashMap captures = locals_and_captures.second(); - - // Null checks - if (args.size() == 0) args = null; - if (locals.size() == 0) locals = null; - if (captures.size() == 0) captures = null; - - return new Triple, Dict, HashMap>(args, locals, captures); - } - - private static ArrayList generateBlockHeaderArgs(TokenQueue tokens) throws ParserException { - ArrayList out = new ArrayList(); - while (tokens.hasNext()) { - Assignment arg = nextArg(tokens); - out.add(arg); - } - return out; - } - - private static Assignment nextArg(TokenQueue tokens) throws EndOfInputError, SyntaxError { - Token current = tokens.next(); - if (current.isa(Token.VAR)) { - VarToken var = (VarToken)current; - boolean copy = false; - Symbol arg_type = null; - - // Copy? - if (tokens.hasNext() && tokens.peek().isa(Token.OP) && tokens.peek().data.equals("$")) { - tokens.next(); // Discard $ - copy = true; - } - - // Type annotation? - if (tokens.hasNext() && tokens.peek().isa(Token.SYMBOL)) { - SymbolToken sym_token = (SymbolToken)tokens.next(); - arg_type = sym_token.getSymbol(); - } - - if (copy || arg_type != null) { - return new TypedAssignment(var.getSourceStringRef(), var.getSymbol(), arg_type, copy); - } else { - return new SimpleAssignment(var.getSourceStringRef(), var.getSymbol()); - } - } else if (current.isa(Token.LIST)) { - ListToken unpack = (ListToken)current; - TokenQueue tq = new TokenQueue(unpack.col); - ArrayList args = new ArrayList(); - Symbol catchall = null; - while (tq.hasNext()) { - // Catch-all - if (tq.peek().isa(Token.COLON)) { - Token colon = tq.next(); // colon - if (tq.hasNext() && tq.peek().isa(Token.VAR)) { - Token var = tq.next(); - catchall = ((VarToken)var).getSymbol(); - } else { - throw new SyntaxError("Expected varname after catchall assignment", colon.getSourceStringRef()); - } - - if (tq.hasNext()) { - throw new SyntaxError("Catch-all name must be last", current.getSourceStringRef()); - } - } else { - Assignment a = nextArg(tq); - boolean slurp = false; - if (tq.hasNext() && tq.peek().isa(Token.OP) && tq.peek().data.equals("~")) { - slurp = true; - tq.next(); // Skip ~ - } - - args.add(new UnpackAssignment.Arg(a, slurp)); - } - } - - if (args.size() == 0) { - throw new SyntaxError("Unpack args must contain at least one element", current.getSourceStringRef()); - } else { - UnpackAssignment ua = UnpackAssignment.fromArgList(args, catchall, current.getSourceStringRef()); - return ua; - } - } else { - throw new SyntaxError("All arguments should follow the format name[$][::type]", current.getSourceStringRef()); - } - } - - /** Assumes args have already been set - * @param captures - * @throws ParserException */ - private static Pair> generateBlockHeaderDefaults(TokenQueue tokens) throws ParserException { - Dict locals = new Dict(); - HashMap captures = new HashMap(); - - while (tokens.hasNext()) { - Token current = tokens.next(); - if (current.isa(VAR)) { - VarToken var = (VarToken)current; - if (!tokens.hasNext() || tokens.peek().isa(Token.VAR)) { - locals.set(var.getSymbol(), Num.ZERO); - } else if (tokens.peek().isa(Token.LAMBDA)){ - LambdaToken lambda = (LambdaToken)tokens.next(); - captures.put(var.getSymbol(), BlockUtils.fromIS(lambda.generateInstructionsForFirst())); - } else if (tokens.peek().isa(Token.OP)) { - OperatorToken opt = (OperatorToken)tokens.next(); - if (opt.data.equals("^")) { - Instruction i = new QuoteGetVariableInstruction(current.getSourceStringRef(), var.getSymbol()); - StaticBlock b = BlockUtils.makeBlockWithSingleInstruction(i); - captures.put(var.getSymbol(), b); - } else { - generateBlockHeaderDefaultsError(current.getSourceStringRef()); - } - } else { - generateBlockHeaderDefaultsError(current.getSourceStringRef()); - } - } else { - generateBlockHeaderDefaultsError(current.getSourceStringRef()); - } - } - - return new Pair>(locals, captures); - } - - private static void generateBlockHeaderDefaultsError(SourceStringRef source) throws SyntaxError { - throw new SyntaxError("All variable initializers should follow the format name()", source); - } - - - - /** Split a single tokenQueue into two at the location of the colon - * t1 contains all tokens before the colon - * t2 contains all tokens after the colon - * both may be empty if there was nothing before/after the colon - * the colon is not included in any - * @param tokens - * @return - * @throws SyntaxError - */ - private static Pair splitAtColon(TokenQueue tokens) throws SyntaxError { - ArrayList ts = tokens.getArrayList(); - int colons = 0; - int colon_index = 0; - for (int i = 0; i < ts.size(); i++) { - if (ts.get(i).isa(Token.COLON)) { - colon_index = i; - colons++; - } - } - - if (colons == 0) { - return new Pair(tokens, new TokenQueue()); - } else if (colons > 1) { - throw new SyntaxError("Expected only one colon (:) token in blockEvaluator header", tokens.peek().getSourceStringRef()); - } else { - ArrayList t1 = new ArrayList(colon_index); - ArrayList t2 = new ArrayList(ts.size()-colon_index); - for (int i = 0; i < colon_index; i++) { - t1.add(ts.get(i)); - } - // colon_index+1 skip the colon itself - for (int i = colon_index+1; i < ts.size(); i++) { - t2.add(ts.get(i)); - } - - return new Pair(new TokenQueue(t1), - new TokenQueue(t2)); - } - } @Override diff --git a/src/aya/parser/tokens/CollectionToken.java b/src/aya/parser/tokens/CollectionToken.java index 97a584ef..a491c7b9 100644 --- a/src/aya/parser/tokens/CollectionToken.java +++ b/src/aya/parser/tokens/CollectionToken.java @@ -13,6 +13,10 @@ public CollectionToken(int type, String data, ArrayList col, SourceString this.col = col; } + public ArrayList getCol() { + return col; + } + /** Splits a list of tokens wherever a comma is */ protected static ArrayList splitCommas(ArrayList tokens) { ArrayList out = new ArrayList(); diff --git a/src/aya/parser/tokens/DictToken.java b/src/aya/parser/tokens/DictToken.java index cbbb62e0..72f40e49 100644 --- a/src/aya/parser/tokens/DictToken.java +++ b/src/aya/parser/tokens/DictToken.java @@ -1,6 +1,7 @@ package aya.parser.tokens; import java.util.ArrayList; +import java.util.HashMap; import aya.exceptions.parser.ParserException; import aya.exceptions.parser.SyntaxError; @@ -8,10 +9,16 @@ import aya.instruction.EmptyDictLiteralInstruction; import aya.instruction.Instruction; import aya.instruction.InstructionStack; +import aya.instruction.variable.assignment.Assignment; import aya.obj.block.BlockUtils; +import aya.obj.block.StaticBlock; +import aya.obj.dict.Dict; +import aya.obj.symbol.Symbol; +import aya.parser.HeaderUtils; import aya.parser.Parser; import aya.parser.SourceStringRef; import aya.parser.token.TokenQueue; +import aya.util.Triple; public class DictToken extends CollectionToken { @@ -24,43 +31,32 @@ public Instruction getInstruction() throws ParserException { //Split Tokens where there are commas ArrayList blockData = splitCommas(col); + // No header, normal dict literal if (blockData.size() == 1) { InstructionStack instructions = Parser.generate(blockData.get(0)); + + // Optimization for empty dict literal if (instructions.size() == 0) { return EmptyDictLiteralInstruction.INSTANCE; } else { return new DictLiteralInstruction(this.getSourceStringRef(), BlockUtils.fromIS(instructions)); } + } else if (blockData.size() == 2) { TokenQueue header = blockData.get(0); - - // Single number in header, create a dict factory with a capture - if (header.size() == 1 && header.peek() instanceof NumberToken) { - - NumberToken nt = (NumberToken)header.peek(); - int n = 0; - try { - n = nt.numValue().toInt(); - } catch (NumberFormatException e) { - throw new SyntaxError(nt + " is not a valid number in the blockEvaluator header", nt.getSourceStringRef()); - } - - if (n < 1) { - throw new SyntaxError("Cannot capture less than 1 elements from outer stack in a dict literal", nt.getSourceStringRef()); - } - InstructionStack instructions = Parser.generate(blockData.get(1)); - if (n == 0 && instructions.isEmpty()) { - return EmptyDictLiteralInstruction.INSTANCE; - } else { - return new DictLiteralInstruction(this.getSourceStringRef(), BlockUtils.fromIS(instructions), n); - } + + if (header.size() > 0) { + InstructionStack main_instructions = Parser.generate(blockData.get(1)); + Triple, Dict, HashMap> p = HeaderUtils.generateBlockHeader(blockData.get(0)); + StaticBlock blk = BlockUtils.fromIS(main_instructions, p.second(), p.first()); + return new DictLiteralInstruction(this.getSourceStringRef(), blk, p.third()); } else { - throw new SyntaxError("dict headers not supported (yet)", source); + throw new SyntaxError("Empty header not allowed in dict literal", source); } - } else { - throw new SyntaxError("Dict headers not supported (yet)", source); + throw new SyntaxError("Dict literal has too many parts", source); } + } @Override diff --git a/std/matrix.aya b/std/matrix.aya index cc21d9d5..1218bddd 100644 --- a/std/matrix.aya +++ b/std/matrix.aya @@ -23,7 +23,7 @@ def matrix::__init__ {input::list self, .# Use this instead of the constructor to avoid .# validating the list def matrix::_new {matrix, - :{1, :rows} matrix :M + :{rows,} matrix :M } .{ Example: diff --git a/std/mp.aya b/std/mp.aya index 92187e5c..35d113d3 100644 --- a/std/mp.aya +++ b/std/mp.aya @@ -5,10 +5,6 @@ module mp export ::mp -.#? ::list mp.capture_vars\n given a list of symbols, return a dict with each symbol and -def mp::capture_vars ( {}.M._capture_vars.` ) - - .#? ::list mp.merge\n Convert a list of blocks into a single block def mp::merge { #.op .*